├── .gitattributes ├── .githooks └── pre-commit ├── .github ├── CODEOWNERS ├── dependabot.yml ├── stale.yml └── workflows │ ├── build.yml │ ├── lint.yml │ └── release.yml ├── .gitignore ├── .golangci.yaml ├── .goreleaser.yml ├── .svu.yaml ├── LICENSE ├── Makefile ├── README.md ├── add.go ├── add_test.go ├── chglog_test.go ├── cmd └── chglog │ ├── config.go │ └── main.go ├── files.go ├── format.go ├── format_test.go ├── git.go ├── go.mod ├── go.sum ├── init.go ├── init_test.go ├── order_test.go ├── pkg └── commands │ ├── add.go │ ├── commands.go │ ├── config.go │ ├── format.go │ ├── init.go │ └── version.go ├── templates.go ├── testdata ├── TestFormatChangelog-deb ├── TestFormatChangelog-release ├── TestFormatChangelog-repo ├── TestFormatChangelog-rpm ├── add-repo-annotated-tag │ ├── HEAD │ ├── config │ ├── description │ ├── info │ │ └── exclude │ ├── objects │ │ ├── 43 │ │ │ └── 3581c28b23426dee2c498da28defa9f5bd4f63 │ │ ├── 55 │ │ │ └── 2682b593e1bea86425fd6d9520f2687b28487b │ │ ├── 09 │ │ │ └── 5b7b431351080c1edfe7de73db893e6b59ddce │ │ ├── 2c │ │ │ ├── 499787328348f09ae1e8f03757c6483b9a938a │ │ │ └── c00abc77d401a541d18c26e5c7fbef1effd3ed │ │ ├── 3e │ │ │ └── c1e9a60d07cc060cee727c97ffc8aac5713943 │ │ ├── 6f │ │ │ ├── 59e5e770be445518775b22bbe25e89339b778b │ │ │ └── 6c178ef5f187bb7d4088a0415ac5a9ff0aef45 │ │ ├── ab │ │ │ └── 515204e62e6fd2b468736de7aadcaca4d9dffd │ │ ├── c2 │ │ │ └── b01f193085b4bc020ef62b93d9dc44d9eca7a4 │ │ ├── e6 │ │ │ └── 9de29bb2d1d6434b8b29ae775ad8c2e48c5391 │ │ └── ea │ │ │ └── 2b12e15f99419047f49d5fc5dc844073899da8 │ └── refs │ │ ├── heads │ │ └── master │ │ └── tags │ │ ├── v0.0.1 │ │ └── v0.0.2 ├── add-repo │ ├── HEAD │ ├── config │ ├── description │ ├── hooks │ │ ├── applypatch-msg.sample │ │ ├── commit-msg.sample │ │ ├── fsmonitor-watchman.sample │ │ ├── post-update.sample │ │ ├── pre-applypatch.sample │ │ ├── pre-commit.sample │ │ ├── pre-push.sample │ │ ├── pre-rebase.sample │ │ ├── pre-receive.sample │ │ ├── prepare-commit-msg.sample │ │ └── update.sample │ ├── info │ │ └── exclude │ ├── objects │ │ ├── 43 │ │ │ └── 3581c28b23426dee2c498da28defa9f5bd4f63 │ │ ├── 2c │ │ │ ├── 499787328348f09ae1e8f03757c6483b9a938a │ │ │ └── c00abc77d401a541d18c26e5c7fbef1effd3ed │ │ ├── 3e │ │ │ └── c1e9a60d07cc060cee727c97ffc8aac5713943 │ │ ├── 6f │ │ │ └── 59e5e770be445518775b22bbe25e89339b778b │ │ ├── ab │ │ │ └── 515204e62e6fd2b468736de7aadcaca4d9dffd │ │ ├── c2 │ │ │ └── b01f193085b4bc020ef62b93d9dc44d9eca7a4 │ │ ├── e6 │ │ │ └── 9de29bb2d1d6434b8b29ae775ad8c2e48c5391 │ │ └── ea │ │ │ └── 2b12e15f99419047f49d5fc5dc844073899da8 │ └── refs │ │ ├── heads │ │ └── master │ │ └── tags │ │ └── v0.0.1 ├── gold-add-changelog-with-annotated-commit.yml ├── gold-add-changelog.yml ├── gold-init-changelog-with-merge-commit.yml ├── gold-init-changelog-without-merge-commit.yml ├── gold-init-changelog.yml ├── gold-order-changelog.yml ├── init-repo-with-merge-commit │ ├── HEAD │ ├── config │ ├── description │ ├── hooks │ │ ├── applypatch-msg.sample │ │ ├── commit-msg.sample │ │ ├── fsmonitor-watchman.sample │ │ ├── post-update.sample │ │ ├── pre-applypatch.sample │ │ ├── pre-commit.sample │ │ ├── pre-push.sample │ │ ├── pre-rebase.sample │ │ ├── pre-receive.sample │ │ ├── prepare-commit-msg.sample │ │ └── update.sample │ ├── info │ │ └── exclude │ ├── objects │ │ ├── 25 │ │ │ └── c55c716f1f18bb91aafa55f98a02315f59afe6 │ │ ├── 2c │ │ │ ├── 499787328348f09ae1e8f03757c6483b9a938a │ │ │ └── c00abc77d401a541d18c26e5c7fbef1effd3ed │ │ ├── 3e │ │ │ └── c1e9a60d07cc060cee727c97ffc8aac5713943 │ │ ├── 6f │ │ │ └── 59e5e770be445518775b22bbe25e89339b778b │ │ ├── ab │ │ │ └── 515204e62e6fd2b468736de7aadcaca4d9dffd │ │ ├── d3 │ │ │ └── 0b6cdff6d118d97095f39b2982d4fa237d33d1 │ │ ├── e6 │ │ │ ├── 9de29bb2d1d6434b8b29ae775ad8c2e48c5391 │ │ │ └── f123d7d0aa2025ce61d28e2d34f94f3a6b5218 │ │ └── ea │ │ │ └── 2b12e15f99419047f49d5fc5dc844073899da8 │ ├── packed-refs │ └── refs │ │ ├── heads │ │ └── master │ │ └── tags │ │ └── v0.0.1 └── init-repo │ ├── HEAD │ ├── config │ ├── description │ ├── hooks │ ├── applypatch-msg.sample │ ├── commit-msg.sample │ ├── fsmonitor-watchman.sample │ ├── post-update.sample │ ├── pre-applypatch.sample │ ├── pre-commit.sample │ ├── pre-push.sample │ ├── pre-rebase.sample │ ├── pre-receive.sample │ ├── prepare-commit-msg.sample │ └── update.sample │ ├── info │ └── exclude │ ├── objects │ ├── 2c │ │ ├── 499787328348f09ae1e8f03757c6483b9a938a │ │ └── c00abc77d401a541d18c26e5c7fbef1effd3ed │ ├── 3e │ │ └── c1e9a60d07cc060cee727c97ffc8aac5713943 │ ├── 6f │ │ └── 59e5e770be445518775b22bbe25e89339b778b │ ├── ab │ │ └── 515204e62e6fd2b468736de7aadcaca4d9dffd │ ├── e6 │ │ └── 9de29bb2d1d6434b8b29ae775ad8c2e48c5391 │ └── ea │ │ └── 2b12e15f99419047f49d5fc5dc844073899da8 │ └── refs │ ├── heads │ └── master │ └── tags │ └── v0.0.1 └── types.go /.gitattributes: -------------------------------------------------------------------------------- 1 | * text eol=lf 2 | 3 | # Denote all files that are truly binary and should not be modified. 4 | *.png binary 5 | *.jpg binary 6 | *.ico binary -------------------------------------------------------------------------------- /.githooks/pre-commit: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | FILES=$(git diff --staged --diff-filter=AM --no-renames --name-only) 3 | make fmt lint test && git add $FILES 4 | -------------------------------------------------------------------------------- /.github/CODEOWNERS: -------------------------------------------------------------------------------- 1 | * @goreleaser/everyone 2 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: "gomod" 4 | directory: "/" 5 | schedule: 6 | interval: "daily" 7 | time: "08:00" 8 | labels: 9 | - "dependencies" 10 | commit-message: 11 | prefix: "chore" 12 | include: "scope" 13 | - package-ecosystem: "github-actions" 14 | directory: "/" 15 | schedule: 16 | interval: "daily" 17 | time: "08:00" 18 | labels: 19 | - "dependencies" 20 | commit-message: 21 | prefix: "chore" 22 | include: "scope" 23 | - package-ecosystem: "docker" 24 | directory: "/" 25 | schedule: 26 | interval: "daily" 27 | time: "08:00" 28 | labels: 29 | - "dependencies" 30 | commit-message: 31 | prefix: "chore" 32 | include: "scope" 33 | -------------------------------------------------------------------------------- /.github/stale.yml: -------------------------------------------------------------------------------- 1 | # Number of days of inactivity before an issue becomes stale 2 | daysUntilStale: 14 3 | # Number of days of inactivity before a stale issue is closed 4 | daysUntilClose: 7 5 | # Issues with these labels will never be considered stale 6 | exemptLabels: 7 | - pinned 8 | - security 9 | # Label to use when marking an issue as stale 10 | staleLabel: wontfix 11 | # Comment to post when marking an issue as stale. Set to `false` to disable 12 | markComment: > 13 | This issue has been automatically marked as stale because it has not had 14 | recent activity. It will be closed if no further activity occurs. Thank you 15 | for your contributions. 16 | # Comment to post when closing a stale issue. Set to `false` to disable 17 | closeComment: false 18 | -------------------------------------------------------------------------------- /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: build 2 | 3 | on: 4 | push: 5 | branches: 6 | - "main" 7 | pull_request: 8 | 9 | jobs: 10 | build: 11 | strategy: 12 | fail-fast: true 13 | matrix: 14 | os: [ubuntu-latest, windows-latest] 15 | runs-on: ${{ matrix.os }} 16 | steps: 17 | - uses: actions/checkout@v4 18 | - uses: actions/setup-go@v5 19 | with: 20 | go-version: "stable" 21 | - uses: goreleaser/goreleaser-action@v6 22 | with: 23 | install-only: true 24 | - run: goreleaser build --snapshot --clean --snapshot 25 | - run: go test -v -failfast -race -coverpkg=./... -covermode=atomic -coverprofile=coverage.out ./... -timeout=5m 26 | - uses: codecov/codecov-action@v5 27 | with: 28 | token: ${{ secrets.CODECOV_TOKEN }} 29 | file: ./coverage.out 30 | dependabot: 31 | needs: [build] 32 | runs-on: ubuntu-latest 33 | permissions: 34 | pull-requests: write 35 | contents: write 36 | if: ${{ github.actor == 'dependabot[bot]' && github.event_name == 'pull_request'}} 37 | steps: 38 | - id: metadata 39 | uses: dependabot/fetch-metadata@v2 40 | with: 41 | github-token: "${{ secrets.GITHUB_TOKEN }}" 42 | - run: | 43 | gh pr review --approve "$PR_URL" 44 | gh pr merge --squash --auto "$PR_URL" 45 | env: 46 | PR_URL: ${{github.event.pull_request.html_url}} 47 | GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}} 48 | -------------------------------------------------------------------------------- /.github/workflows/lint.yml: -------------------------------------------------------------------------------- 1 | name: golangci-lint 2 | on: 3 | push: 4 | tags: 5 | - v* 6 | branches: 7 | - main 8 | pull_request: 9 | permissions: 10 | contents: read 11 | 12 | jobs: 13 | golangci: 14 | permissions: 15 | contents: read 16 | pull-requests: read 17 | name: lint 18 | runs-on: ubuntu-latest 19 | steps: 20 | - uses: actions/checkout@v4 21 | - uses: actions/setup-go@v5 22 | with: 23 | go-version: "stable" 24 | - name: golangci-lint 25 | uses: golangci/golangci-lint-action@4afd733a84b1f43292c63897423277bb7f4313a9 26 | with: 27 | skip-go-installation: true 28 | args: --timeout=5m 29 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: releaese 2 | 3 | on: 4 | push: 5 | tags: 6 | - "v*" 7 | 8 | jobs: 9 | goreleaser: 10 | runs-on: ubuntu-latest 11 | steps: 12 | - uses: actions/checkout@v4 13 | with: 14 | fetch-depth: 0 15 | - uses: actions/setup-go@v5 16 | with: 17 | go-version: "stable" 18 | - uses: goreleaser/goreleaser-action@v6 19 | with: 20 | version: latest 21 | args: release --clean 22 | env: 23 | GITHUB_TOKEN: ${{ secrets.GH_PAT }} 24 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .idea/ 2 | *.rpm 3 | coverage.txt 4 | dist 5 | .DS_Store 6 | bin 7 | coverage.out 8 | chglog 9 | !chglog/ -------------------------------------------------------------------------------- /.golangci.yaml: -------------------------------------------------------------------------------- 1 | run: 2 | go: "1.22" 3 | timeout: 5m 4 | linters: 5 | enable: 6 | - thelper 7 | - gofumpt 8 | - tparallel 9 | - unconvert 10 | - unparam 11 | - wastedassign 12 | - revive 13 | - forbidigo 14 | - tagliatelle 15 | - misspell 16 | - depguard 17 | - testifylint 18 | - gocritic 19 | linters-settings: 20 | staticcheck: 21 | checks: 22 | - all 23 | - "-SA1019" 24 | forbidigo: 25 | forbid: 26 | - 'ioutil\.*' 27 | tagliatelle: 28 | case: 29 | use-field-name: false 30 | rules: 31 | yaml: snake 32 | json: snake 33 | depguard: 34 | rules: 35 | main: 36 | deny: 37 | - pkg: "github.com/pkg/errors" 38 | desc: "use stdlib instead" 39 | gocritic: 40 | enabled-checks: 41 | - exitAfterDefer 42 | testifylint: 43 | enable-all: true 44 | disable: 45 | - error-is-as # false positive 46 | -------------------------------------------------------------------------------- /.goreleaser.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | 3 | before: 4 | hooks: 5 | - go mod tidy 6 | 7 | gomod: 8 | proxy: true 9 | 10 | builds: 11 | - main: ./cmd/chglog 12 | env: 13 | - CGO_ENABLED=0 14 | goos: 15 | - linux 16 | - darwin 17 | - windows 18 | goarch: 19 | - amd64 20 | - arm64 21 | mod_timestamp: "{{ .CommitTimestamp }}" 22 | flags: 23 | - -trimpath 24 | ldflags: 25 | - -s -w -X main.version={{.Version}} -X main.commit={{.Commit}} -X main.date={{ .CommitDate }} -X main.builtBy=goreleaser 26 | 27 | changelog: 28 | sort: asc 29 | use: github 30 | filters: 31 | exclude: 32 | - "^test:" 33 | - "^chore" 34 | - "merge conflict" 35 | - Merge pull request 36 | - Merge remote-tracking branch 37 | - Merge branch 38 | - go mod tidy 39 | groups: 40 | - title: Dependency updates 41 | regexp: "^.*feat\\(deps\\)*:+.*$" 42 | order: 300 43 | - title: "New Features" 44 | regexp: "^.*feat[(\\w)]*:+.*$" 45 | order: 100 46 | - title: "Bug fixes" 47 | regexp: "^.*fix[(\\w)]*:+.*$" 48 | order: 200 49 | - title: "Documentation updates" 50 | regexp: "^.*docs[(\\w)]*:+.*$" 51 | order: 400 52 | - title: Other work 53 | order: 9999 54 | 55 | archives: 56 | - name_template: >- 57 | {{ .ProjectName }}_ 58 | {{- title .Os }}_ 59 | {{- if eq .Arch "amd64" }}x86_64 60 | {{- else if eq .Arch "386" }}i386 61 | {{- else }}{{ .Arch }}{{ end }} 62 | {{- if .Arm }}v{{ .Arm }}{{ end }} 63 | brews: 64 | - repository: 65 | owner: goreleaser 66 | name: homebrew-tap 67 | directory: Formula 68 | homepage: https://github.com/goreleaser/chglog 69 | description: chglog is a changelog management library and tool 70 | test: | 71 | system "#{bin}/chglog version" 72 | nfpms: 73 | - file_name_template: "{{ .ProjectName }}_{{ .Arch }}" 74 | homepage: https://github.com/goreleaser/chglog 75 | description: chglog is a changelog management library and tool 76 | maintainer: Dj Gilcrease 77 | license: MIT 78 | vendor: GoReleaser 79 | formats: 80 | - deb 81 | - rpm 82 | scoops: 83 | - repository: 84 | owner: goreleaser 85 | name: scoop-bucket 86 | homepage: https://goreleaser.com 87 | directory: bucket 88 | description: Deliver Go binaries as fast and easily as possible 89 | license: MIT 90 | 91 | release: 92 | footer: | 93 | **Full Changelog**: https://github.com/goreleaser/goreleaser/compare/{{ .PreviousTag }}...{{ .Tag }} 94 | 95 | ## What to do next? 96 | 97 | - Read the [documentation](https://goreleaser.com/intro/) 98 | - Check out the [GoReleaser Pro](https://goreleaser.com/pro) distribution 99 | - Join our [Discord server](https://discord.gg/RGEBtg8vQ6) 100 | - Follow us on [Twitter](https://twitter.com/goreleaser) 101 | -------------------------------------------------------------------------------- /.svu.yaml: -------------------------------------------------------------------------------- 1 | # svu configuration. 2 | # 3 | # https://github.com/caarlos0/svu 4 | tag.prefix: "v" 5 | verbose: true 6 | always: true 7 | v0: true 8 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Dj Gilcrease 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | SOURCE_FILES?=./... 2 | TEST_PATTERN?=. 3 | TEST_OPTIONS?= 4 | TEST_TIMEOUT?=5m 5 | 6 | test: 7 | go test $(TEST_OPTIONS) -v -failfast -race -coverpkg=./... -covermode=atomic -coverprofile=coverage.out $(SOURCE_FILES) -run $(TEST_PATTERN) -timeout=$(TEST_TIMEOUT) 8 | .PHONY: test 9 | 10 | cover: test 11 | go tool cover -html=coverage.out 12 | .PHONY: cover 13 | 14 | fmt: 15 | go mod tidy 16 | gofumpt -w -l . 17 | .PHONY: fmt 18 | 19 | ci: build test 20 | .PHONY: ci 21 | 22 | build: 23 | goreleaser build --clean --snapshot --single-target -o chglog 24 | .PHONY: build 25 | 26 | .DEFAULT_GOAL := build 27 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

2 | GoReleaser Logo 3 |

chglog

4 |

chglog is a changelog management library and tool

5 |

6 | Release 7 | Software License 8 | GitHub Actions 11 | Codecov branch 12 | Go Report Card 13 | Go Doc 14 | Powered By: GoReleaser 15 |

16 |

17 | 18 | ## Why 19 | 20 | While there are other tool out there that will create a changelog output as part 21 | of their workflow none of the ones I could find did so in a way that allowed 22 | formatting the output via multiple templates. 23 | 24 | The need to multiple output formats was being driven by the desire to add 25 | changelog support to https://github.com/goreleaser/nfpm and the deb and rpm 26 | changelog formats not being the same. 27 | 28 | ## Goals 29 | 30 | - [x] be simple to use 31 | - [x] provide decent default templates for deb, rpm, release and repository 32 | style changelog formats 33 | - [x] be distributed as a single binary 34 | - [x] reproducible results 35 | - [x] depend on the fewer external things as possible 36 | - [x] store changelog in a transportable format (.yml) 37 | - [x] be possible to use it as a lib in other go projects (namely 38 | [goreleaser](https://goreleaser.com) itself) 39 | 40 | ## Install 41 | 42 | ```bash 43 | go install github.com/goreleaser/chglog/cmd/chglog@latest 44 | ``` 45 | 46 | ## Usage 47 | 48 | The first steps are to run `chglog config` to initialize a configuration file 49 | (`.chglog.yml`) and edit the generated file according to your needs: 50 | 51 | ```yaml 52 | conventional-commits: false 53 | exclude-merge-commits: false 54 | deb: 55 | distribution: [] 56 | urgency: "" 57 | debug: false 58 | owner: "" 59 | package-name: "" 60 | ``` 61 | 62 | The next step is to run `chglog init`. 63 | 64 | ```yaml 65 | - semver: 0.0.1 66 | date: 2019-10-18T16:05:33-07:00 67 | packager: dj gilcrease 68 | changes: 69 | - commit: 2c499787328348f09ae1e8f03757c6483b9a938a 70 | note: |- 71 | oops i forgot to use Conventional Commits style message 72 | 73 | This should NOT break anything even if I am asking to build the changelog using Conventional Commits style message 74 | - commit: 3ec1e9a60d07cc060cee727c97ffc8aac5713943 75 | note: |- 76 | feat: added file two feature 77 | 78 | BREAKING CHANGE: this is a backwards incompatible change 79 | - commit: 2cc00abc77d401a541d18c26e5c7fbef1effd3ed 80 | note: |- 81 | feat: added the fileone feature 82 | 83 | * This is a test repo 84 | * so ya! 85 | ``` 86 | 87 | Then to generate a `CHANGELOG.md` file you would do `chglog format --template 88 | repo > CHANGELOG.md` 89 | 90 | Now whenever you go to do another release you would do `chglog add --version 91 | v#.#.#` (version MUST be semver format) 92 | 93 | And that's it! 94 | 95 | ## Usage as lib 96 | 97 | You can look at the code of chglog itself to see how to use it as a library 98 | 99 | ## Status 100 | 101 | - alpha 102 | 103 | ## Donate 104 | 105 | Donations are very much appreciated! You can donate/sponsor on the main 106 | [goreleaser OpenCollective](https://opencollective.com/goreleaser)! It's 107 | easy and will surely help the developers at least buy some ☕️ or 🍺! 108 | 109 | ## Stargazers over time 110 | 111 | [![goreleaser/chglog stargazers over time](https://starchart.cc/goreleaser/chglog.svg)](https://starchart.cc/goreleaser/chglog) 112 | 113 | --- 114 | 115 | Would you like to fix something in the documentation? Feel free to open an 116 | [issue](https://github.com/goreleaser/chglog/issues). 117 | -------------------------------------------------------------------------------- /add.go: -------------------------------------------------------------------------------- 1 | package chglog 2 | 3 | import ( 4 | "errors" 5 | "fmt" 6 | "regexp" 7 | "sort" 8 | "strings" 9 | "time" 10 | 11 | "github.com/go-git/go-git/v5" 12 | "github.com/go-git/go-git/v5/plumbing" 13 | "github.com/go-git/go-git/v5/plumbing/object" 14 | conventional_commit "gitlab.com/digitalxero/go-conventional-commit" 15 | ) 16 | 17 | // ErrNoCommits happens when no commits are found for a given entry. 18 | var ErrNoCommits = errors.New("no commits found for this entry") 19 | 20 | var signedOffRegEx = regexp.MustCompile(`(?m)(?:^.*Signed-off-by:.*>$)`) 21 | 22 | // AddEntry add a ChangeLog entry to an existing ChangeLogEntries that. 23 | func AddEntry( 24 | gitRepo *git.Repository, 25 | version fmt.Stringer, 26 | owner string, 27 | notes *ChangeLogNotes, 28 | deb *ChangelogDeb, 29 | current ChangeLogEntries, 30 | useConventionalCommits bool, 31 | excludeMergeCommits bool, 32 | ) (cle ChangeLogEntries, err error) { 33 | var ( 34 | ref *plumbing.Reference 35 | from, to plumbing.Hash 36 | commits []*object.Commit 37 | ) 38 | 39 | if ref, err = gitRepo.Head(); err != nil { 40 | return nil, fmt.Errorf("error adding entry: %w", err) 41 | } 42 | from = ref.Hash() 43 | 44 | to = plumbing.ZeroHash 45 | if len(current) > 0 { 46 | if to, err = GitHashFotTag(gitRepo, current[0].Semver); err != nil { 47 | return nil, fmt.Errorf("error adding entry: %w", err) 48 | } 49 | } 50 | 51 | cle = append(cle, current...) 52 | if commits, err = CommitsBetween(gitRepo, from, to, excludeMergeCommits); err != nil { 53 | return nil, fmt.Errorf("error adding entry: %w", err) 54 | } 55 | 56 | if len(commits) == 0 { 57 | return nil, ErrNoCommits 58 | } 59 | 60 | cle = append(cle, CreateEntry(time.Now(), version, owner, notes, deb, commits, useConventionalCommits)) 61 | sort.Sort(sort.Reverse(cle)) 62 | 63 | return cle, nil 64 | } 65 | 66 | func processMsg(msg string) string { 67 | msg = strings.ReplaceAll(strings.ReplaceAll(msg, "\r\n\r\n", "\n\n"), "\r", "") 68 | msg = signedOffRegEx.ReplaceAllString(msg, "") 69 | msg = strings.ReplaceAll(strings.Trim(msg, "\n"), "\n\n\n", "\n") 70 | 71 | return msg 72 | } 73 | 74 | // CreateEntry create a ChangeLog object. 75 | func CreateEntry(date time.Time, version fmt.Stringer, owner string, notes *ChangeLogNotes, deb *ChangelogDeb, commits []*object.Commit, useConventionalCommits bool) (changelog *ChangeLog) { 76 | var cc *conventional_commit.ConventionalCommit 77 | changelog = &ChangeLog{ 78 | Semver: version.String(), 79 | Date: date, 80 | Packager: owner, 81 | Notes: notes, 82 | } 83 | if len(commits) == 0 { 84 | return 85 | } 86 | changelog.Changes = make(ChangeLogChanges, len(commits)) 87 | changelog.Deb = deb 88 | 89 | for idx, c := range commits { 90 | msg := processMsg(c.Message) 91 | if useConventionalCommits { 92 | cc = conventional_commit.ParseConventionalCommit(msg) 93 | } 94 | changelog.Changes[idx] = &ChangeLogChange{ 95 | Commit: c.Hash.String(), 96 | Note: msg, 97 | Committer: &User{ 98 | Name: c.Committer.Name, 99 | Email: c.Committer.Email, 100 | }, 101 | Author: &User{ 102 | Name: c.Author.Name, 103 | Email: c.Author.Email, 104 | }, 105 | ConventionalCommit: cc, 106 | } 107 | } 108 | 109 | return changelog 110 | } 111 | -------------------------------------------------------------------------------- /add_test.go: -------------------------------------------------------------------------------- 1 | package chglog 2 | 3 | import ( 4 | "log" 5 | "testing" 6 | 7 | "github.com/Masterminds/semver/v3" 8 | "github.com/go-git/go-git/v5" 9 | "github.com/smartystreets/goconvey/convey" 10 | ) 11 | 12 | func TestAddEntry(t *testing.T) { 13 | t.Run("lightweight tag", func(t *testing.T) { 14 | testAddEntry(t, "./testdata/add-repo", "./testdata/gold-add-changelog.yml") 15 | }) 16 | t.Run("annotated tag", func(t *testing.T) { 17 | testAddEntry(t, "./testdata/add-repo-annotated-tag", "./testdata/gold-add-changelog-with-annotated-commit.yml") 18 | }) 19 | } 20 | 21 | func testAddEntry(t *testing.T, repo, goldclePath string) { 22 | t.Helper() 23 | var ( 24 | err error 25 | gitRepo *git.Repository 26 | testCLE ChangeLogEntries 27 | ) 28 | goldcle, err := Parse(goldclePath) 29 | if err != nil { 30 | t.Fatal(err) 31 | } 32 | 33 | if gitRepo, err = GitRepo(repo, false); err != nil { 34 | log.Fatal(err) 35 | } 36 | 37 | testCLE, err = InitChangelog(gitRepo, "", nil, nil, true, false) 38 | if err != nil { 39 | t.Fatal(err) 40 | } 41 | version, _ := semver.NewVersion("1.0.0-b1+git.123") 42 | header := ` 43 | This is a test 44 | ====== 45 | 46 | header entry 47 | ` 48 | testCLE, err = AddEntry(gitRepo, version, "Dj Gilcrease", createNote(header, ""), nil, testCLE, true, false) 49 | if err != nil { 50 | t.Fatal(err) 51 | } 52 | if len(goldcle) != len(testCLE) { 53 | t.Fatal("differing results") 54 | } 55 | 56 | // Fix the date since AddEntry uses time.Now 57 | for i, e := range goldcle { 58 | testCLE[i].Date = e.Date 59 | } 60 | convey.Convey("Generated entry should be the same as the golden entry", t, func() { 61 | convey.So(testCLE, convey.ShouldResemble, goldcle) 62 | }) 63 | } 64 | -------------------------------------------------------------------------------- /chglog_test.go: -------------------------------------------------------------------------------- 1 | package chglog 2 | 3 | func createNote(header, footer string) *ChangeLogNotes { 4 | if header == "" && footer == "" { 5 | return nil 6 | } 7 | ret := &ChangeLogNotes{} 8 | if header != "" { 9 | ret.Header = &header 10 | } 11 | if footer != "" { 12 | ret.Footer = &footer 13 | } 14 | 15 | return ret 16 | } 17 | -------------------------------------------------------------------------------- /cmd/chglog/config.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "strings" 5 | 6 | "github.com/spf13/viper" 7 | ) 8 | 9 | func setupConfig(cfgFile string) *viper.Viper { 10 | config := viper.New() 11 | config.SetEnvPrefix(pkgName) 12 | config.AutomaticEnv() 13 | config.SetEnvKeyReplacer(strings.NewReplacer(".", "_", "-", "")) 14 | config.SetDefault("debug", false) 15 | config.SetConfigFile(cfgFile) 16 | _ = config.ReadInConfig() 17 | config.Set("app.name", pkgName) 18 | config.Set("app.version", version) 19 | config.Set("app.commit", commit) 20 | 21 | return config 22 | } 23 | -------------------------------------------------------------------------------- /cmd/chglog/main.go: -------------------------------------------------------------------------------- 1 | // Package main contains the main nfpm cli source code. 2 | package main 3 | 4 | import ( 5 | "fmt" 6 | "os" 7 | "path" 8 | 9 | "github.com/goreleaser/chglog/pkg/commands" 10 | "github.com/spf13/cobra" 11 | ) 12 | 13 | // nolint: gochecknoglobals, gocritic 14 | var ( 15 | pkgName = "chglog" 16 | version = "v0.0.0" 17 | commit = "local" 18 | ) 19 | 20 | func main() { 21 | cmdRoot := &cobra.Command{ 22 | Use: pkgName, 23 | Short: "Changelog generator", 24 | SilenceUsage: true, 25 | } 26 | cwd, _ := os.Getwd() 27 | cfgFile := path.Join(cwd, fmt.Sprintf(".%s.yml", pkgName)) 28 | config := setupConfig(cfgFile) 29 | 30 | cmdRoot.PersistentFlags().StringVarP( 31 | &cfgFile, 32 | "config-file", 33 | "c", 34 | cfgFile, 35 | ``) 36 | _ = config.BindPFlag("config-file", cmdRoot.PersistentFlags().Lookup("config-file")) 37 | 38 | cmdRoot.PersistentPreRunE = func(*cobra.Command, []string) error { 39 | if cfgFile == config.GetString("config-file") { 40 | return nil 41 | } 42 | 43 | config.SetConfigFile(cfgFile) 44 | config.Set("config-file", cfgFile) 45 | return config.ReadInConfig() 46 | } 47 | 48 | cmdRoot.AddCommand( 49 | commands.AddCmd(config), 50 | commands.InitCmd(config), 51 | commands.VersionCmd(config), 52 | commands.ConfigCmd(config), 53 | commands.FormatCmd(config), 54 | ) 55 | 56 | if err := cmdRoot.Execute(); err != nil { 57 | // nolint: gomnd, gocritic 58 | os.Exit(127) 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /files.go: -------------------------------------------------------------------------------- 1 | package chglog 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | 7 | "gopkg.in/yaml.v3" 8 | ) 9 | 10 | // Parse parse a changelog.yml into ChangeLogEntries. 11 | func Parse(file string) (entries ChangeLogEntries, err error) { 12 | var body []byte 13 | body, err = os.ReadFile(file) // nolint: gosec,gocritic 14 | switch { 15 | case os.IsNotExist(err): 16 | return make(ChangeLogEntries, 0), nil 17 | case err != nil: 18 | return nil, fmt.Errorf("error parsing %s: %w", file, err) 19 | } 20 | 21 | if err = yaml.Unmarshal(body, &entries); err != nil { 22 | return entries, fmt.Errorf("error parsing %s: %w", file, err) 23 | } 24 | 25 | return entries, nil 26 | } 27 | 28 | // Save save ChangeLogEntries to a yml file. 29 | func (c *ChangeLogEntries) Save(file string) (err error) { 30 | data, _ := yaml.Marshal(c) 31 | // nolint: gosec,gocritic 32 | return os.WriteFile(file, data, 0o644) 33 | } 34 | -------------------------------------------------------------------------------- /format.go: -------------------------------------------------------------------------------- 1 | package chglog 2 | 3 | import ( 4 | "bytes" 5 | "fmt" 6 | "text/template" 7 | ) 8 | 9 | // FormatChangelog format pkgLogs from a text/template. 10 | func FormatChangelog(pkgLogs *PackageChangeLog, tpl *template.Template) (string, error) { 11 | var data bytes.Buffer 12 | if err := tpl.Execute(&data, pkgLogs); err != nil { 13 | return data.String(), fmt.Errorf("error formatting: %w", err) 14 | } 15 | 16 | return data.String(), nil 17 | } 18 | -------------------------------------------------------------------------------- /format_test.go: -------------------------------------------------------------------------------- 1 | package chglog 2 | 3 | import ( 4 | "fmt" 5 | "log" 6 | "os" 7 | "testing" 8 | 9 | "github.com/go-git/go-git/v5" 10 | "github.com/google/go-cmp/cmp" 11 | ) 12 | 13 | // nolint: gochecknoglobals,gocritic 14 | var formats = map[string]string{"rpm": rpmTpl, "deb": debTpl, "release": releaseTpl, "repo": repoTpl} 15 | 16 | func TestFormatChangelog(t *testing.T) { 17 | var ( 18 | err error 19 | gitRepo *git.Repository 20 | testCLE ChangeLogEntries 21 | ) 22 | t.Parallel() 23 | if gitRepo, err = GitRepo("./testdata/init-repo", false); err != nil { 24 | log.Fatal(err) 25 | } 26 | 27 | if testCLE, err = InitChangelog(gitRepo, "", nil, nil, true, false); err != nil { 28 | t.Error(err) 29 | 30 | return 31 | } 32 | 33 | for tmplType, tmplData := range formats { 34 | tmplData := tmplData 35 | pkg := PackageChangeLog{fmt.Sprintf("TestFormatChangelog-%s", tmplType), testCLE} 36 | t.Run(tmplType, func(t *testing.T) { 37 | t.Parallel() 38 | accept(t, tmplData, pkg) 39 | }) 40 | } 41 | } 42 | 43 | func accept(t *testing.T, tmplData string, pkg PackageChangeLog) { 44 | t.Helper() 45 | 46 | tpl, err := LoadTemplateData(tmplData) 47 | if err != nil { 48 | t.Error(err) 49 | 50 | return 51 | } 52 | 53 | testdata, err := FormatChangelog(&pkg, tpl) 54 | if err != nil { 55 | t.Error(err) 56 | 57 | return 58 | } 59 | 60 | golddata, _ := os.ReadFile(fmt.Sprintf("./testdata/%s", pkg.Name)) 61 | if diff := cmp.Diff(string(golddata), testdata); diff != "" { 62 | t.Errorf("FormatChangelog mismatch (+got -want):\n%s", diff) 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /git.go: -------------------------------------------------------------------------------- 1 | package chglog 2 | 3 | import ( 4 | "errors" 5 | "fmt" 6 | "strings" 7 | 8 | "github.com/go-git/go-git/v5" 9 | "github.com/go-git/go-git/v5/plumbing" 10 | "github.com/go-git/go-git/v5/plumbing/object" 11 | ) 12 | 13 | var errReachedToCommit = errors.New("reached to commit") 14 | 15 | // GitRepo open a GitRepo to use to build the changelog from. 16 | func GitRepo(gitPath string, detectDotGit bool) (*git.Repository, error) { 17 | return git.PlainOpenWithOptions(gitPath, &git.PlainOpenOptions{ 18 | DetectDotGit: detectDotGit, 19 | }) 20 | } 21 | 22 | // GitHashFotTag return the git sha for a particular tag. 23 | func GitHashFotTag(gitRepo *git.Repository, tagName string) (hash plumbing.Hash, err error) { 24 | var ref *plumbing.Reference 25 | ref, err = gitRepo.Tag(tagName) 26 | if errors.Is(err, git.ErrTagNotFound) && !strings.HasPrefix(tagName, "v") { 27 | ref, err = gitRepo.Tag("v" + tagName) 28 | } 29 | if err != nil { 30 | return plumbing.ZeroHash, fmt.Errorf("error getting commit for tag %s: %w", tagName, err) 31 | } 32 | 33 | // If the tag is annotated, we need to grab the actual commit hash. 34 | obj, err := gitRepo.TagObject(ref.Hash()) 35 | if err != nil { 36 | // Not an annotated tag 37 | return ref.Hash(), nil 38 | } 39 | 40 | commit, err := obj.Commit() 41 | if err != nil { 42 | return plumbing.ZeroHash, fmt.Errorf("error getting commit for tag %s: %w", tagName, err) 43 | } 44 | 45 | return commit.Hash, nil 46 | } 47 | 48 | // CommitsBetween return the list of commits between two commits. 49 | func CommitsBetween(gitRepo *git.Repository, start, end plumbing.Hash, excludeMergeCommits bool) (commits []*object.Commit, err error) { 50 | var commitIter object.CommitIter 51 | if commitIter, err = gitRepo.Log(&git.LogOptions{ 52 | From: start, 53 | Order: git.LogOrderCommitterTime, 54 | }); err != nil { 55 | return nil, fmt.Errorf("error getting commits between %v & %v: %w", start, end, err) 56 | } 57 | defer commitIter.Close() 58 | err = commitIter.ForEach(func(c *object.Commit) error { 59 | // If no previous tag is found then from and to are equal 60 | if end == start { 61 | return nil 62 | } 63 | if c.Hash == end { 64 | return errReachedToCommit 65 | } 66 | if excludeMergeCommits && len(c.ParentHashes) > 1 { 67 | return nil 68 | } 69 | 70 | commits = append(commits, c) 71 | 72 | return nil 73 | }) 74 | 75 | if err != nil && !errors.Is(err, errReachedToCommit) { 76 | return nil, fmt.Errorf("error getting commits between %v & %v: %w", start, end, err) 77 | } 78 | 79 | return commits, nil 80 | } 81 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/goreleaser/chglog 2 | 3 | go 1.23.0 4 | 5 | require ( 6 | github.com/Masterminds/semver/v3 v3.3.1 7 | github.com/Masterminds/sprig/v3 v3.3.0 8 | github.com/go-git/go-billy/v5 v5.6.2 9 | github.com/go-git/go-git/v5 v5.16.1 10 | github.com/google/go-cmp v0.7.0 11 | github.com/smartystreets/goconvey v1.8.1 12 | github.com/spf13/cobra v1.9.1 13 | github.com/spf13/viper v1.20.1 14 | gitlab.com/digitalxero/go-conventional-commit v1.0.7 15 | gopkg.in/yaml.v3 v3.0.1 16 | ) 17 | 18 | require ( 19 | dario.cat/mergo v1.0.1 // indirect 20 | github.com/Masterminds/goutils v1.1.1 // indirect 21 | github.com/Microsoft/go-winio v0.6.2 // indirect 22 | github.com/ProtonMail/go-crypto v1.1.6 // indirect 23 | github.com/cloudflare/circl v1.6.1 // indirect 24 | github.com/cyphar/filepath-securejoin v0.4.1 // indirect 25 | github.com/emirpasic/gods v1.18.1 // indirect 26 | github.com/fsnotify/fsnotify v1.8.0 // indirect 27 | github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 // indirect 28 | github.com/go-viper/mapstructure/v2 v2.2.1 // indirect 29 | github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8 // indirect 30 | github.com/google/uuid v1.6.0 // indirect 31 | github.com/gopherjs/gopherjs v1.17.2 // indirect 32 | github.com/huandu/xstrings v1.5.0 // indirect 33 | github.com/inconshreveable/mousetrap v1.1.0 // indirect 34 | github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect 35 | github.com/jtolds/gls v4.20.0+incompatible // indirect 36 | github.com/kevinburke/ssh_config v1.2.0 // indirect 37 | github.com/mitchellh/copystructure v1.2.0 // indirect 38 | github.com/mitchellh/reflectwalk v1.0.2 // indirect 39 | github.com/pelletier/go-toml/v2 v2.2.3 // indirect 40 | github.com/pjbgf/sha1cd v0.3.2 // indirect 41 | github.com/sagikazarmark/locafero v0.7.0 // indirect 42 | github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3 // indirect 43 | github.com/shopspring/decimal v1.4.0 // indirect 44 | github.com/skeema/knownhosts v1.3.1 // indirect 45 | github.com/smarty/assertions v1.15.0 // indirect 46 | github.com/sourcegraph/conc v0.3.0 // indirect 47 | github.com/spf13/afero v1.12.0 // indirect 48 | github.com/spf13/cast v1.7.1 // indirect 49 | github.com/spf13/pflag v1.0.6 // indirect 50 | github.com/subosito/gotenv v1.6.0 // indirect 51 | github.com/xanzy/ssh-agent v0.3.3 // indirect 52 | go.uber.org/atomic v1.9.0 // indirect 53 | go.uber.org/multierr v1.9.0 // indirect 54 | golang.org/x/crypto v0.37.0 // indirect 55 | golang.org/x/net v0.39.0 // indirect 56 | golang.org/x/sys v0.32.0 // indirect 57 | golang.org/x/text v0.24.0 // indirect 58 | gopkg.in/warnings.v0 v0.1.2 // indirect 59 | ) 60 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | dario.cat/mergo v1.0.1 h1:Ra4+bf83h2ztPIQYNP99R6m+Y7KfnARDfID+a+vLl4s= 2 | dario.cat/mergo v1.0.1/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk= 3 | github.com/Masterminds/goutils v1.1.1 h1:5nUrii3FMTL5diU80unEVvNevw1nH4+ZV4DSLVJLSYI= 4 | github.com/Masterminds/goutils v1.1.1/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU= 5 | github.com/Masterminds/semver/v3 v3.3.1 h1:QtNSWtVZ3nBfk8mAOu/B6v7FMJ+NHTIgUPi7rj+4nv4= 6 | github.com/Masterminds/semver/v3 v3.3.1/go.mod h1:4V+yj/TJE1HU9XfppCwVMZq3I84lprf4nC11bSS5beM= 7 | github.com/Masterminds/sprig/v3 v3.3.0 h1:mQh0Yrg1XPo6vjYXgtf5OtijNAKJRNcTdOOGZe3tPhs= 8 | github.com/Masterminds/sprig/v3 v3.3.0/go.mod h1:Zy1iXRYNqNLUolqCpL4uhk6SHUMAOSCzdgBfDb35Lz0= 9 | github.com/Microsoft/go-winio v0.5.2/go.mod h1:WpS1mjBmmwHBEWmogvA2mj8546UReBk4v8QkMxJ6pZY= 10 | github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY= 11 | github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU= 12 | github.com/ProtonMail/go-crypto v1.1.6 h1:ZcV+Ropw6Qn0AX9brlQLAUXfqLBc7Bl+f/DmNxpLfdw= 13 | github.com/ProtonMail/go-crypto v1.1.6/go.mod h1:rA3QumHc/FZ8pAHreoekgiAbzpNsfQAosU5td4SnOrE= 14 | github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be h1:9AeTilPcZAjCFIImctFaOjnTIavg87rW78vTPkQqLI8= 15 | github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be/go.mod h1:ySMOLuWl6zY27l47sB3qLNK6tF2fkHG55UZxx8oIVo4= 16 | github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio= 17 | github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs= 18 | github.com/cloudflare/circl v1.6.1 h1:zqIqSPIndyBh1bjLVVDHMPpVKqp8Su/V+6MeDzzQBQ0= 19 | github.com/cloudflare/circl v1.6.1/go.mod h1:uddAzsPgqdMAYatqJ0lsjX1oECcQLIlRpzZh3pJrofs= 20 | github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g= 21 | github.com/cyphar/filepath-securejoin v0.4.1 h1:JyxxyPEaktOD+GAnqIqTf9A8tHyAG22rowi7HkoSU1s= 22 | github.com/cyphar/filepath-securejoin v0.4.1/go.mod h1:Sdj7gXlvMcPZsbhwhQ33GguGLDGQL7h7bg04C/+u9jI= 23 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 24 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= 25 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 26 | github.com/elazarl/goproxy v1.7.2 h1:Y2o6urb7Eule09PjlhQRGNsqRfPmYI3KKQLFpCAV3+o= 27 | github.com/elazarl/goproxy v1.7.2/go.mod h1:82vkLNir0ALaW14Rc399OTTjyNREgmdL2cVoIbS6XaE= 28 | github.com/emirpasic/gods v1.18.1 h1:FXtiHYKDGKCW2KzwZKx0iC0PQmdlorYgdFG9jPXJ1Bc= 29 | github.com/emirpasic/gods v1.18.1/go.mod h1:8tpGGwCnJ5H4r6BWwaV6OrWmMoPhUl5jm/FMNAnJvWQ= 30 | github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8= 31 | github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0= 32 | github.com/fsnotify/fsnotify v1.8.0 h1:dAwr6QBTBZIkG8roQaJjGof0pp0EeF+tNV7YBP3F/8M= 33 | github.com/fsnotify/fsnotify v1.8.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0= 34 | github.com/gliderlabs/ssh v0.3.8 h1:a4YXD1V7xMF9g5nTkdfnja3Sxy1PVDCj1Zg4Wb8vY6c= 35 | github.com/gliderlabs/ssh v0.3.8/go.mod h1:xYoytBv1sV0aL3CavoDuJIQNURXkkfPA/wxQ1pL1fAU= 36 | github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 h1:+zs/tPmkDkHx3U66DAb0lQFJrpS6731Oaa12ikc+DiI= 37 | github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376/go.mod h1:an3vInlBmSxCcxctByoQdvwPiA7DTK7jaaFDBTtu0ic= 38 | github.com/go-git/go-billy/v5 v5.6.2 h1:6Q86EsPXMa7c3YZ3aLAQsMA0VlWmy43r6FHqa/UNbRM= 39 | github.com/go-git/go-billy/v5 v5.6.2/go.mod h1:rcFC2rAsp/erv7CMz9GczHcuD0D32fWzH+MJAU+jaUU= 40 | github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20231010084843-55a94097c399 h1:eMje31YglSBqCdIqdhKBW8lokaMrL3uTkpGYlE2OOT4= 41 | github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20231010084843-55a94097c399/go.mod h1:1OCfN199q1Jm3HZlxleg+Dw/mwps2Wbk9frAWm+4FII= 42 | github.com/go-git/go-git/v5 v5.16.1 h1:TuxMBWNL7R05tXsUGi0kh1vi4tq0WfXNLlIrAkXG1k8= 43 | github.com/go-git/go-git/v5 v5.16.1/go.mod h1:4Ge4alE/5gPs30F2H1esi2gPd69R0C39lolkucHBOp8= 44 | github.com/go-viper/mapstructure/v2 v2.2.1 h1:ZAaOCxANMuZx5RCeg0mBdEZk7DZasvvZIxtHqx8aGss= 45 | github.com/go-viper/mapstructure/v2 v2.2.1/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM= 46 | github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8 h1:f+oWsMOmNPc8JmEHVZIycC7hBoQxHH9pNKQORJNozsQ= 47 | github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8/go.mod h1:wcDNUvekVysuuOpQKo3191zZyTpiI6se1N1ULghS0sw= 48 | github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= 49 | github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= 50 | github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= 51 | github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= 52 | github.com/gopherjs/gopherjs v1.17.2 h1:fQnZVsXk8uxXIStYb0N4bGk7jeyTalG/wsZjQ25dO0g= 53 | github.com/gopherjs/gopherjs v1.17.2/go.mod h1:pRRIvn/QzFLrKfvEz3qUuEhtE/zLCWfreZ6J5gM2i+k= 54 | github.com/huandu/xstrings v1.5.0 h1:2ag3IFq9ZDANvthTwTiqSSZLjDc+BedvHPAp5tJy2TI= 55 | github.com/huandu/xstrings v1.5.0/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE= 56 | github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= 57 | github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= 58 | github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 h1:BQSFePA1RWJOlocH6Fxy8MmwDt+yVQYULKfN0RoTN8A= 59 | github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo= 60 | github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo= 61 | github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= 62 | github.com/kevinburke/ssh_config v1.2.0 h1:x584FjTGwHzMwvHx18PXxbBVzfnxogHaAReU4gf13a4= 63 | github.com/kevinburke/ssh_config v1.2.0/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM= 64 | github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= 65 | github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= 66 | github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= 67 | github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= 68 | github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= 69 | github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= 70 | github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= 71 | github.com/mitchellh/copystructure v1.2.0 h1:vpKXTN4ewci03Vljg/q9QvCGUDttBOGBIa15WveJJGw= 72 | github.com/mitchellh/copystructure v1.2.0/go.mod h1:qLl+cE2AmVv+CoeAwDPye/v+N2HKCj9FbZEVFJRxO9s= 73 | github.com/mitchellh/reflectwalk v1.0.2 h1:G2LzWKi524PWgd3mLHV8Y5k7s6XUvT0Gef6zxSIeXaQ= 74 | github.com/mitchellh/reflectwalk v1.0.2/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= 75 | github.com/onsi/gomega v1.34.1 h1:EUMJIKUjM8sKjYbtxQI9A4z2o+rruxnzNvpknOXie6k= 76 | github.com/onsi/gomega v1.34.1/go.mod h1:kU1QgUvBDLXBJq618Xvm2LUX6rSAfRaFRTcdOeDLwwY= 77 | github.com/pelletier/go-toml/v2 v2.2.3 h1:YmeHyLY8mFWbdkNWwpr+qIL2bEqT0o95WSdkNHvL12M= 78 | github.com/pelletier/go-toml/v2 v2.2.3/go.mod h1:MfCQTFTvCcUyyvvwm1+G6H/jORL20Xlb6rzQu9GuUkc= 79 | github.com/pjbgf/sha1cd v0.3.2 h1:a9wb0bp1oC2TGwStyn0Umc/IGKQnEgF0vVaZ8QF8eo4= 80 | github.com/pjbgf/sha1cd v0.3.2/go.mod h1:zQWigSxVmsHEZow5qaLtPYxpcKMMQpa09ixqBxuCS6A= 81 | github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= 82 | github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 83 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 84 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 85 | github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= 86 | github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= 87 | github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= 88 | github.com/sagikazarmark/locafero v0.7.0 h1:5MqpDsTGNDhY8sGp0Aowyf0qKsPrhewaLSsFaodPcyo= 89 | github.com/sagikazarmark/locafero v0.7.0/go.mod h1:2za3Cg5rMaTMoG/2Ulr9AwtFaIppKXTRYnozin4aB5k= 90 | github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3 h1:n661drycOFuPLCN3Uc8sB6B/s6Z4t2xvBgU1htSHuq8= 91 | github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3/go.mod h1:A0bzQcvG0E7Rwjx0REVgAGH58e96+X0MeOfepqsbeW4= 92 | github.com/shopspring/decimal v1.4.0 h1:bxl37RwXBklmTi0C79JfXCEBD1cqqHt0bbgBAGFp81k= 93 | github.com/shopspring/decimal v1.4.0/go.mod h1:gawqmDU56v4yIKSwfBSFip1HdCCXN8/+DMd9qYNcwME= 94 | github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= 95 | github.com/skeema/knownhosts v1.3.1 h1:X2osQ+RAjK76shCbvhHHHVl3ZlgDm8apHEHFqRjnBY8= 96 | github.com/skeema/knownhosts v1.3.1/go.mod h1:r7KTdC8l4uxWRyK2TpQZ/1o5HaSzh06ePQNxPwTcfiY= 97 | github.com/smarty/assertions v1.15.0 h1:cR//PqUBUiQRakZWqBiFFQ9wb8emQGDb0HeGdqGByCY= 98 | github.com/smarty/assertions v1.15.0/go.mod h1:yABtdzeQs6l1brC900WlRNwj6ZR55d7B+E8C6HtKdec= 99 | github.com/smartystreets/goconvey v1.8.1 h1:qGjIddxOk4grTu9JPOU31tVfq3cNdBlNa5sSznIX1xY= 100 | github.com/smartystreets/goconvey v1.8.1/go.mod h1:+/u4qLyY6x1jReYOp7GOM2FSt8aP9CzCZL03bI28W60= 101 | github.com/sourcegraph/conc v0.3.0 h1:OQTbbt6P72L20UqAkXXuLOj79LfEanQ+YQFNpLA9ySo= 102 | github.com/sourcegraph/conc v0.3.0/go.mod h1:Sdozi7LEKbFPqYX2/J+iBAM6HpqSLTASQIKqDmF7Mt0= 103 | github.com/spf13/afero v1.12.0 h1:UcOPyRBYczmFn6yvphxkn9ZEOY65cpwGKb5mL36mrqs= 104 | github.com/spf13/afero v1.12.0/go.mod h1:ZTlWwG4/ahT8W7T0WQ5uYmjI9duaLQGy3Q2OAl4sk/4= 105 | github.com/spf13/cast v1.7.1 h1:cuNEagBQEHWN1FnbGEjCXL2szYEXqfJPbP2HNUaca9Y= 106 | github.com/spf13/cast v1.7.1/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo= 107 | github.com/spf13/cobra v1.9.1 h1:CXSaggrXdbHK9CF+8ywj8Amf7PBRmPCOJugH954Nnlo= 108 | github.com/spf13/cobra v1.9.1/go.mod h1:nDyEzZ8ogv936Cinf6g1RU9MRY64Ir93oCnqb9wxYW0= 109 | github.com/spf13/pflag v1.0.6 h1:jFzHGLGAlb3ruxLB8MhbI6A8+AQX/2eW4qeyNZXNp2o= 110 | github.com/spf13/pflag v1.0.6/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= 111 | github.com/spf13/viper v1.20.1 h1:ZMi+z/lvLyPSCoNtFCpqjy0S4kPbirhpTMwl8BkW9X4= 112 | github.com/spf13/viper v1.20.1/go.mod h1:P9Mdzt1zoHIG8m2eZQinpiBjo6kCmZSKBClNNqjJvu4= 113 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 114 | github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= 115 | github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= 116 | github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= 117 | github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= 118 | github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= 119 | github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8= 120 | github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU= 121 | github.com/xanzy/ssh-agent v0.3.3 h1:+/15pJfg/RsTxqYcX6fHqOXZwwMP+2VyYWJeWM2qQFM= 122 | github.com/xanzy/ssh-agent v0.3.3/go.mod h1:6dzNDKs0J9rVPHPhaGCukekBHKqfl+L3KghI1Bc68Uw= 123 | gitlab.com/digitalxero/go-conventional-commit v1.0.7 h1:8/dO6WWG+98PMhlZowt/YjuiKhqhGlOCwlIV8SqqGh8= 124 | gitlab.com/digitalxero/go-conventional-commit v1.0.7/go.mod h1:05Xc2BFsSyC5tKhK0y+P3bs0AwUtNuTp+mTpbCU/DZ0= 125 | go.uber.org/atomic v1.9.0 h1:ECmE8Bn/WFTYwEW/bpKD3M8VtR/zQVbavAoalC1PYyE= 126 | go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= 127 | go.uber.org/multierr v1.9.0 h1:7fIwc/ZtS0q++VgcfqFDxSBZVv/Xo49/SYnDFupUwlI= 128 | go.uber.org/multierr v1.9.0/go.mod h1:X2jQV1h+kxSjClGpnseKVIxpmcjrj7MNnI0bnlfKTVQ= 129 | golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= 130 | golang.org/x/crypto v0.37.0 h1:kJNSjF/Xp7kU0iB2Z+9viTPMW4EqqsrywMXLJOOsXSE= 131 | golang.org/x/crypto v0.37.0/go.mod h1:vg+k43peMZ0pUMhYmVAWysMK35e6ioLh3wB8ZCAfbVc= 132 | golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 h1:2dVuKD2vS7b0QIHQbpyTISPd0LeHDbnYEryqj5Q1ug8= 133 | golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56/go.mod h1:M4RDyNAINzryxdtnbRXRL/OHtkFuWGRjvuhBJpk2IlY= 134 | golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= 135 | golang.org/x/net v0.39.0 h1:ZCu7HMWDxpXpaiKdhzIfaltL9Lp31x/3fCP11bc6/fY= 136 | golang.org/x/net v0.39.0/go.mod h1:X7NRbYVEA+ewNkCNyJ513WmMdQ3BineSwVtN2zD/d+E= 137 | golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 138 | golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 139 | golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 140 | golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 141 | golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 142 | golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 143 | golang.org/x/sys v0.32.0 h1:s77OFDvIQeibCmezSnk/q6iAfkdiQaJi4VzroCFrN20= 144 | golang.org/x/sys v0.32.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= 145 | golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= 146 | golang.org/x/term v0.31.0 h1:erwDkOK1Msy6offm1mOgvspSkslFnIGsFnxOKoufg3o= 147 | golang.org/x/term v0.31.0/go.mod h1:R4BeIy7D95HzImkxGkTW1UQTtP54tio2RyHz7PwK0aw= 148 | golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= 149 | golang.org/x/text v0.24.0 h1:dd5Bzh4yt5KYA8f9CJHCP4FB4D51c2c6JvN37xJJkJ0= 150 | golang.org/x/text v0.24.0/go.mod h1:L8rBsPeo2pSS+xqN0d5u2ikmjtmoJbDBT1b7nHvFCdU= 151 | golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 152 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 153 | gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 154 | gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= 155 | gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= 156 | gopkg.in/warnings.v0 v0.1.2 h1:wFXVbFY8DY5/xOe1ECiWdKCzZlxgshcYVNkBHstARME= 157 | gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI= 158 | gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 159 | gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= 160 | gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= 161 | gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 162 | -------------------------------------------------------------------------------- /init.go: -------------------------------------------------------------------------------- 1 | package chglog 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | "sort" 7 | 8 | "github.com/Masterminds/semver/v3" 9 | "github.com/go-git/go-git/v5" 10 | "github.com/go-git/go-git/v5/plumbing" 11 | "github.com/go-git/go-git/v5/plumbing/object" 12 | ) 13 | 14 | func versionsInRepo(gitRepo *git.Repository) (map[plumbing.Hash]*semver.Version, error) { 15 | tagRefs, err := gitRepo.Tags() 16 | if err != nil { 17 | return nil, err 18 | } 19 | 20 | defer tagRefs.Close() 21 | 22 | tags := make(map[plumbing.Hash]*semver.Version) 23 | 24 | err = tagRefs.ForEach(func(t *plumbing.Reference) error { 25 | var ( 26 | version *semver.Version 27 | tag *object.Tag 28 | ) 29 | 30 | tagName := t.Name().Short() 31 | hash := t.Hash() 32 | 33 | if version, err = semver.NewVersion(tagName); err != nil || version == nil { 34 | fmt.Fprintf(os.Stderr, "Warning: unable to parse version from tag: %s : %v\n", tagName, err) 35 | return nil 36 | } 37 | 38 | // If this is an annotated tag look up the hash of the commit and use that. 39 | if tag, err = gitRepo.TagObject(t.Hash()); err == nil { 40 | var c *object.Commit 41 | 42 | if c, err = tag.Commit(); err != nil { 43 | return fmt.Errorf("cannot dereference annotated tag: %s : %w", tagName, err) 44 | } 45 | hash = c.Hash 46 | } 47 | 48 | tags[hash] = version 49 | 50 | return nil 51 | }) 52 | if err != nil { 53 | return nil, err 54 | } 55 | 56 | return tags, nil 57 | } 58 | 59 | func versionsOnBranch(gitRepo *git.Repository) (map[*semver.Version]plumbing.Hash, error) { 60 | repoVersions, err := versionsInRepo(gitRepo) 61 | if err != nil { 62 | return nil, err 63 | } 64 | 65 | refs, err := gitRepo.Log(&git.LogOptions{}) 66 | if err != nil { 67 | return nil, err 68 | } 69 | 70 | defer refs.Close() 71 | 72 | versions := make(map[*semver.Version]plumbing.Hash) 73 | 74 | err = refs.ForEach(func(c *object.Commit) error { 75 | if v, ok := repoVersions[c.Hash]; ok { 76 | versions[v] = c.Hash 77 | } 78 | return nil 79 | }) 80 | 81 | return versions, err 82 | } 83 | 84 | // InitChangelog create a new ChangeLogEntries from a git repo. 85 | func InitChangelog(gitRepo *git.Repository, owner string, notes *ChangeLogNotes, deb *ChangelogDeb, useConventionalCommits bool, excludeMergeCommits bool) (cle ChangeLogEntries, err error) { 86 | var start, end plumbing.Hash 87 | 88 | cle = make(ChangeLogEntries, 0) 89 | end = plumbing.ZeroHash 90 | 91 | versions, err := versionsOnBranch(gitRepo) 92 | if err != nil { 93 | return nil, err 94 | } 95 | 96 | tags := make([]*semver.Version, 0, len(versions)) 97 | for v := range versions { 98 | tags = append(tags, v) 99 | } 100 | 101 | sort.Slice(tags, func(i, j int) bool { return tags[i].LessThan(tags[j]) }) 102 | 103 | for _, version := range tags { 104 | var ( 105 | commits []*object.Commit 106 | commitObject *object.Commit 107 | ) 108 | 109 | if version.Prerelease() != "" { 110 | // Do not need change logs for pre-release entries 111 | continue 112 | } 113 | 114 | start = versions[version] 115 | 116 | if commitObject, err = gitRepo.CommitObject(start); err != nil { 117 | return nil, fmt.Errorf("unable to fetch commit from tag %v: %w", version.Original(), err) 118 | } 119 | 120 | if owner == "" { 121 | owner = fmt.Sprintf("%s <%s>", commitObject.Committer.Name, commitObject.Committer.Email) 122 | } 123 | if commits, err = CommitsBetween(gitRepo, start, end, excludeMergeCommits); err != nil { 124 | return nil, fmt.Errorf("unable to find commits between %s & %s: %w", end, start, err) 125 | } 126 | 127 | changelog := CreateEntry(commitObject.Committer.When, version, owner, notes, deb, commits, useConventionalCommits) 128 | cle = append(cle, changelog) 129 | end = start 130 | } 131 | 132 | sort.Sort(sort.Reverse(cle)) 133 | 134 | return cle, nil 135 | } 136 | -------------------------------------------------------------------------------- /init_test.go: -------------------------------------------------------------------------------- 1 | package chglog 2 | 3 | import ( 4 | "log" 5 | "testing" 6 | 7 | "github.com/go-git/go-git/v5" 8 | "github.com/smartystreets/goconvey/convey" 9 | ) 10 | 11 | func TestInitChangelog(t *testing.T) { 12 | var ( 13 | err error 14 | gitRepo *git.Repository 15 | testCLE ChangeLogEntries 16 | ) 17 | 18 | goldcle, err := Parse("./testdata/gold-init-changelog.yml") 19 | if err != nil { 20 | t.Fatal(err) 21 | } 22 | if gitRepo, err = GitRepo("./testdata/init-repo", false); err != nil { 23 | log.Fatal(err) 24 | } 25 | 26 | testCLE, err = InitChangelog(gitRepo, "", nil, nil, true, false) 27 | if err != nil { 28 | t.Fatal(err) 29 | } 30 | if len(goldcle) != len(testCLE) { 31 | t.Fatal("differing results") 32 | } 33 | 34 | // Fix the date since AddEntry uses time.Now 35 | for i, e := range goldcle { 36 | testCLE[i].Date = e.Date 37 | } 38 | convey.Convey("Generated entry should be the same as the golden entry", t, func() { 39 | convey.So(testCLE, convey.ShouldResemble, goldcle) 40 | }) 41 | } 42 | 43 | func TestInitChangelogWithoutMergeCommits(t *testing.T) { 44 | var ( 45 | err error 46 | gitRepo *git.Repository 47 | testCLE ChangeLogEntries 48 | ) 49 | 50 | goldcle, err := Parse("./testdata/gold-init-changelog-without-merge-commit.yml") 51 | if err != nil { 52 | t.Fatal(err) 53 | } 54 | if gitRepo, err = GitRepo("./testdata/init-repo-with-merge-commit", false); err != nil { 55 | log.Fatal(err) 56 | } 57 | 58 | testCLE, err = InitChangelog(gitRepo, "", nil, nil, true, true) 59 | if err != nil { 60 | t.Fatal(err) 61 | } 62 | if len(goldcle) != len(testCLE) { 63 | t.Fatal("differing results") 64 | } 65 | 66 | // Fix the date since AddEntry uses time.Now 67 | for i, e := range goldcle { 68 | testCLE[i].Date = e.Date 69 | } 70 | convey.Convey("Generated entry should be the same as the golden entry", t, func() { 71 | convey.So(testCLE, convey.ShouldResemble, goldcle) 72 | }) 73 | } 74 | 75 | func TestInitChangelogWithMergeCommits(t *testing.T) { 76 | var ( 77 | err error 78 | gitRepo *git.Repository 79 | testCLE ChangeLogEntries 80 | ) 81 | 82 | goldcle, err := Parse("./testdata/gold-init-changelog-with-merge-commit.yml") 83 | if err != nil { 84 | t.Fatal(err) 85 | } 86 | if gitRepo, err = GitRepo("./testdata/init-repo-with-merge-commit", false); err != nil { 87 | log.Fatal(err) 88 | } 89 | 90 | testCLE, err = InitChangelog(gitRepo, "", nil, nil, true, false) 91 | if err != nil { 92 | t.Fatal(err) 93 | } 94 | if len(goldcle) != len(testCLE) { 95 | t.Fatal("differing results") 96 | } 97 | 98 | // Fix the date since AddEntry uses time.Now 99 | for i, e := range goldcle { 100 | testCLE[i].Date = e.Date 101 | } 102 | convey.Convey("Generated entry should be the same as the golden entry", t, func() { 103 | convey.So(testCLE, convey.ShouldResemble, goldcle) 104 | }) 105 | } 106 | -------------------------------------------------------------------------------- /order_test.go: -------------------------------------------------------------------------------- 1 | package chglog 2 | 3 | import ( 4 | "fmt" 5 | "log" 6 | "os" 7 | "testing" 8 | "time" 9 | 10 | "github.com/go-git/go-billy/v5" 11 | "github.com/go-git/go-billy/v5/memfs" 12 | "github.com/go-git/go-git/v5" 13 | "github.com/go-git/go-git/v5/plumbing" 14 | "github.com/go-git/go-git/v5/plumbing/object" 15 | "github.com/go-git/go-git/v5/storage/memory" 16 | "github.com/smartystreets/goconvey/convey" 17 | ) 18 | 19 | type testRepo struct { 20 | Git *git.Repository 21 | Source *git.Worktree 22 | seqno int 23 | } 24 | 25 | func newTestRepo() *testRepo { 26 | var ( 27 | repo *git.Repository 28 | tree *git.Worktree 29 | err error 30 | ) 31 | 32 | fs := memfs.New() 33 | 34 | if repo, err = git.Init(memory.NewStorage(), fs); err != nil { 35 | log.Fatal(err) 36 | } 37 | 38 | if tree, err = repo.Worktree(); err != nil { 39 | log.Fatal(err) 40 | } 41 | 42 | return &testRepo{ 43 | Git: repo, 44 | Source: tree, 45 | } 46 | } 47 | 48 | // modifyAndCommit creates the file if it does not exist, appends a 49 | // change, commits the file, and returns the hash of the commit. 50 | func (r *testRepo) modifyAndCommit(opts *git.CommitOptions) plumbing.Hash { 51 | filename := "file" 52 | var ( 53 | hash plumbing.Hash 54 | err error 55 | file billy.File 56 | ) 57 | 58 | if file, err = r.Source.Filesystem.OpenFile(filename, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0o666); err != nil { 59 | log.Fatal(err) 60 | } 61 | 62 | if _, err = fmt.Fprintf(file, "commit %d\n", r.seqno); err != nil { 63 | log.Fatal(err) 64 | } 65 | _ = file.Close() 66 | 67 | if _, err = r.Source.Add(filename); err != nil { 68 | log.Fatal(err) 69 | } 70 | 71 | if hash, err = r.Source.Commit(fmt.Sprintf("commit %d", r.seqno), opts); err != nil { 72 | log.Fatal(err) 73 | } 74 | 75 | r.seqno++ 76 | 77 | return hash 78 | } 79 | 80 | func defSignature() *object.Signature { 81 | tm, err := time.Parse(time.RFC3339, "2000-01-01T12:00:00+07:00") 82 | if err != nil { 83 | tm = time.Now() 84 | } 85 | 86 | return &object.Signature{ 87 | Name: "John Doe", 88 | Email: "John.Doe@example.com", 89 | When: tm, 90 | } 91 | } 92 | 93 | func defCommitOptions() *git.CommitOptions { 94 | return &git.CommitOptions{ 95 | Author: defSignature(), 96 | Committer: defSignature(), 97 | } 98 | } 99 | 100 | func TestOrderChangelog(t *testing.T) { 101 | goldCLE, err := Parse("./testdata/gold-order-changelog.yml") 102 | if err != nil { 103 | t.Fatal(err) 104 | } 105 | 106 | repo := newTestRepo() 107 | 108 | for i := 0; i <= 10; i++ { 109 | hash := repo.modifyAndCommit(defCommitOptions()) 110 | 111 | if _, err = repo.Git.CreateTag(fmt.Sprintf("v0.%d.0", i), hash, nil); err != nil { 112 | t.Fatal(err) 113 | } 114 | } 115 | 116 | testCLE, err := InitChangelog(repo.Git, "", nil, nil, false, false) 117 | if err != nil { 118 | t.Fatal(err) 119 | } 120 | 121 | convey.Convey("Generated entry should be the same as the golden entry", t, func() { 122 | convey.So(testCLE, convey.ShouldResemble, goldCLE) 123 | }) 124 | } 125 | 126 | func TestOffBranchTags(t *testing.T) { 127 | cle, err := Parse("./testdata/gold-order-changelog.yml") 128 | if err != nil { 129 | t.Fatal(err) 130 | } 131 | 132 | // remove odd entries 133 | 134 | goldCLE := make(ChangeLogEntries, len(cle)/2+1) 135 | for i := range cle { 136 | if i%2 == 0 { 137 | goldCLE[i/2] = cle[i] 138 | } 139 | } 140 | 141 | repo := newTestRepo() 142 | tree, err := repo.Git.Worktree() 143 | if err != nil { 144 | t.Fatal(err) 145 | } 146 | 147 | // initial commit on master 148 | 149 | hash := repo.modifyAndCommit(defCommitOptions()) 150 | if _, err = repo.Git.CreateTag("v0.0.0", hash, nil); err != nil { 151 | t.Fatal(err) 152 | } 153 | 154 | // second commit on develop 155 | 156 | err = tree.Checkout(&git.CheckoutOptions{ 157 | Branch: plumbing.NewBranchReferenceName("develop"), 158 | Create: true, 159 | }) 160 | if err != nil { 161 | t.Fatal(err) 162 | } 163 | 164 | hash = repo.modifyAndCommit(defCommitOptions()) 165 | if _, err = repo.Git.CreateTag("v0.1.0", hash, nil); err != nil { 166 | t.Fatal(err) 167 | } 168 | 169 | // alternate branches for commits 170 | 171 | master := plumbing.NewBranchReferenceName("master") 172 | develop := plumbing.NewBranchReferenceName("develop") 173 | 174 | for i := 2; i <= 10; i++ { 175 | branch := master 176 | if i%2 != 0 { 177 | branch = develop 178 | } 179 | 180 | t.Logf("%v branch=%v\n", i, branch) 181 | 182 | err = tree.Checkout(&git.CheckoutOptions{Branch: branch}) 183 | if err != nil { 184 | t.Fatal(err) 185 | } 186 | 187 | hash := repo.modifyAndCommit(defCommitOptions()) 188 | 189 | if _, err = repo.Git.CreateTag(fmt.Sprintf("v0.%d.0", i), hash, nil); err != nil { 190 | t.Fatal(err) 191 | } 192 | } 193 | 194 | testCLE, err := InitChangelog(repo.Git, "", nil, nil, false, false) 195 | if err != nil { 196 | t.Fatal(err) 197 | } 198 | 199 | // zero all commit hashes (don't care about these) 200 | 201 | for i := range testCLE { 202 | for j := range testCLE[i].Changes { 203 | testCLE[i].Changes[j].Commit = "" 204 | } 205 | } 206 | for i := range goldCLE { 207 | for j := range goldCLE[i].Changes { 208 | goldCLE[i].Changes[j].Commit = "" 209 | } 210 | } 211 | 212 | convey.Convey("Generated entry should be the same as the golden entry", t, func() { 213 | convey.So(testCLE, convey.ShouldResemble, goldCLE) 214 | }) 215 | } 216 | 217 | func TestSemverTag(t *testing.T) { 218 | repo := newTestRepo() 219 | tag := "1.0.0" 220 | 221 | convey.Convey("Semver tags should be parsed", t, func() { 222 | hash := repo.modifyAndCommit(defCommitOptions()) 223 | 224 | if _, err := repo.Git.CreateTag(tag, hash, nil); err != nil { 225 | t.Fatal(err) 226 | } 227 | 228 | cle, err := InitChangelog(repo.Git, "", nil, nil, false, false) 229 | if err != nil { 230 | t.Fatal(err) 231 | } 232 | 233 | convey.So(cle, convey.ShouldHaveLength, 1) 234 | convey.So(cle[0].Semver, convey.ShouldEqual, tag) 235 | }) 236 | 237 | convey.Convey("Not Semver tags should be ignored", t, func() { 238 | hash := repo.modifyAndCommit(defCommitOptions()) 239 | 240 | if _, err := repo.Git.CreateTag("text", hash, nil); err != nil { 241 | t.Fatal(err) 242 | } 243 | 244 | cle, err := InitChangelog(repo.Git, "", nil, nil, false, false) 245 | if err != nil { 246 | t.Fatal(err) 247 | } 248 | 249 | convey.So(cle, convey.ShouldHaveLength, 1) 250 | convey.So(cle[0].Semver, convey.ShouldEqual, tag) 251 | }) 252 | } 253 | -------------------------------------------------------------------------------- /pkg/commands/add.go: -------------------------------------------------------------------------------- 1 | package commands 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | "path/filepath" 7 | "strings" 8 | 9 | "github.com/Masterminds/semver/v3" 10 | "github.com/go-git/go-git/v5" 11 | "github.com/spf13/cobra" 12 | "github.com/spf13/viper" 13 | 14 | "github.com/goreleaser/chglog" 15 | ) 16 | 17 | // nolint: gocognit, funlen, gocritic 18 | func AddCmd(config *viper.Viper) (cmd *cobra.Command) { 19 | var ( 20 | input, 21 | output, 22 | version, 23 | header, 24 | footer, 25 | headerFile, 26 | footerFile string 27 | semv *semver.Version 28 | ) 29 | cmd, config = commonFlags(&cobra.Command{ 30 | Use: "add [PATH]", 31 | Short: "add new changelog entry for [PATH]", 32 | Args: cobra.MaximumNArgs(1), 33 | }, config) 34 | 35 | cmd.Flags().StringVarP( 36 | &output, 37 | "output", 38 | "o", 39 | "changelog.yml", 40 | "file to save the updated changelog to") 41 | cmd.Flags().StringVarP( 42 | &input, 43 | "input", 44 | "i", 45 | "changelog.yml", 46 | "starting changelog.yml file") 47 | cmd.Flags().StringVarP( 48 | &version, 49 | "version", 50 | "", 51 | "", 52 | "Version for this entry") 53 | cmd.Flags().StringVarP( 54 | &header, 55 | "header", 56 | "", 57 | "", 58 | "Header note for this entry") 59 | cmd.Flags().StringVarP( 60 | &footer, 61 | "footer", 62 | "", 63 | "", 64 | "Footer note for this entry") 65 | cmd.Flags().StringVarP( 66 | &headerFile, 67 | "header-file", 68 | "", 69 | "", 70 | "Header note for this entry") 71 | cmd.Flags().StringVarP( 72 | &footerFile, 73 | "footer-file", 74 | "", 75 | "", 76 | "Footer note for this entry") 77 | 78 | cmd.RunE = func(_ *cobra.Command, args []string) (err error) { 79 | var ( 80 | repoPath string 81 | gitRepo *git.Repository 82 | entries chglog.ChangeLogEntries 83 | notes *chglog.ChangeLogNotes 84 | data []byte 85 | ) 86 | 87 | if repoPath, err = os.Getwd(); err != nil { 88 | return fmt.Errorf("error adding entry: %w", err) 89 | } 90 | 91 | if len(args) == 1 { 92 | if repoPath, err = filepath.Abs(args[0]); err != nil { 93 | return fmt.Errorf("error adding entry: %w", err) 94 | } 95 | } 96 | 97 | if entries, err = chglog.Parse(input); err != nil { 98 | return fmt.Errorf("error adding entry: %w", err) 99 | } 100 | 101 | if gitRepo, err = chglog.GitRepo(repoPath, true); err != nil { 102 | return fmt.Errorf("error adding entry: %w", err) 103 | } 104 | 105 | if headerFile != "" { 106 | // nolint: gosec, gocritic 107 | if data, err = os.ReadFile(headerFile); err != nil { 108 | return fmt.Errorf("error adding entry: %w", err) 109 | } 110 | header = string(data) 111 | } 112 | 113 | if footerFile != "" { 114 | // nolint: gosec, gocritic 115 | if data, err = os.ReadFile(footerFile); err != nil { 116 | return fmt.Errorf("error adding entry: %w", err) 117 | } 118 | footer = string(data) 119 | } 120 | 121 | if header != "" || footer != "" { 122 | notes = &chglog.ChangeLogNotes{} 123 | if header != "" { 124 | header = strings.ReplaceAll(header, "\\n", "\n") 125 | notes.Header = &header 126 | } 127 | if footer != "" { 128 | notes.Footer = &footer 129 | } 130 | } 131 | 132 | if semv, err = semver.NewVersion(version); err != nil { 133 | return fmt.Errorf("error adding entry: %w", err) 134 | } 135 | 136 | if entries, err = chglog.AddEntry(gitRepo, semv, config.GetString("owner"), notes, getDeb(config), entries, config.GetBool("conventional-commits"), config.GetBool("exclude-merge-commits")); err != nil { 137 | return fmt.Errorf("error adding entry: %w", err) 138 | } 139 | 140 | if len(entries) == 0 { 141 | return fmt.Errorf("%w: %s", ErrNoTags, repoPath) 142 | } 143 | 144 | return entries.Save(output) 145 | } 146 | 147 | return cmd 148 | } 149 | -------------------------------------------------------------------------------- /pkg/commands/commands.go: -------------------------------------------------------------------------------- 1 | // Package commands contain the commands for the cli 2 | package commands 3 | 4 | import ( 5 | "github.com/goreleaser/chglog" 6 | "github.com/spf13/cobra" 7 | "github.com/spf13/viper" 8 | ) 9 | 10 | func commonFlags(cmd *cobra.Command, config *viper.Viper) (*cobra.Command, *viper.Viper) { 11 | var ( 12 | urgency, owner string 13 | distribution []string 14 | conventionalCommits bool 15 | excludeMergeCommits bool 16 | ) 17 | cmd.Flags().BoolVarP( 18 | &conventionalCommits, 19 | "conventional-commits", 20 | "", 21 | conventionalCommits, 22 | `Use conventional commits parsing`) 23 | cmd.Flags().BoolVarP( 24 | &excludeMergeCommits, 25 | "exclude-merge-commits", 26 | "", 27 | excludeMergeCommits, 28 | `Exclude merge commits`) 29 | cmd.Flags().StringVarP( 30 | &owner, 31 | "owner", 32 | "", 33 | owner, 34 | `set package owner`) 35 | cmd.Flags().StringVarP( 36 | &urgency, 37 | "deb-urgency", 38 | "", 39 | urgency, 40 | `set debian urgency for`) 41 | cmd.Flags().StringSliceVarP( 42 | &distribution, 43 | "deb-distribution", 44 | "", 45 | distribution, 46 | `set debian distributions for`) 47 | 48 | cmd.PreRunE = func(*cobra.Command, []string) error { 49 | if err := config.BindPFlag("conventional-commits", cmd.Flags().Lookup("conventional-commits")); err != nil { 50 | return err 51 | } 52 | if err := config.BindPFlag("exclude-merge-commits", cmd.Flags().Lookup("exclude-merge-commits")); err != nil { 53 | return err 54 | } 55 | if err := config.BindPFlag("owner", cmd.Flags().Lookup("owner")); err != nil { 56 | return err 57 | } 58 | if err := config.BindPFlag("deb.urgency", cmd.Flags().Lookup("deb-urgency")); err != nil { 59 | return err 60 | } 61 | return config.BindPFlag("deb.distribution", cmd.Flags().Lookup("deb-distribution")) 62 | } 63 | 64 | return cmd, config 65 | } 66 | 67 | func getDeb(config *viper.Viper) (deb *chglog.ChangelogDeb) { 68 | var ( 69 | urgency string 70 | distributions []string 71 | ) 72 | urgency = config.GetString("deb.urgency") 73 | distributions = config.GetStringSlice("deb.distribution") 74 | if len(distributions) > 0 && urgency != "" { 75 | deb = &chglog.ChangelogDeb{ 76 | Urgency: urgency, 77 | Distributions: distributions, 78 | } 79 | } 80 | 81 | return deb 82 | } 83 | -------------------------------------------------------------------------------- /pkg/commands/config.go: -------------------------------------------------------------------------------- 1 | package commands 2 | 3 | import ( 4 | "github.com/spf13/cobra" 5 | "github.com/spf13/viper" 6 | ) 7 | 8 | func ConfigCmd(config *viper.Viper) (cmd *cobra.Command) { 9 | var pkg string 10 | cmd = &cobra.Command{ 11 | Use: "config", 12 | Short: "save config data", 13 | } 14 | 15 | cmd, config = commonFlags(cmd, config) 16 | cmd.Flags().StringVarP( 17 | &pkg, 18 | "package-name", 19 | "p", 20 | "", 21 | "package name to use in formatting") 22 | 23 | cmd.PreRunE = func(cmd *cobra.Command, _ []string) error { 24 | return config.BindPFlag("package-name", cmd.Flags().Lookup("package-name")) 25 | } 26 | 27 | cmd.RunE = func(*cobra.Command, []string) error { 28 | // Filter some config settings 29 | cfgMap := config.AllSettings() 30 | delete(cfgMap, "app") 31 | delete(cfgMap, "config-file") 32 | v := viper.New() 33 | if err := v.MergeConfigMap(cfgMap); err != nil { 34 | return err 35 | } 36 | 37 | return v.WriteConfigAs(config.GetString("config-file")) 38 | } 39 | 40 | return cmd 41 | } 42 | -------------------------------------------------------------------------------- /pkg/commands/format.go: -------------------------------------------------------------------------------- 1 | package commands 2 | 3 | import ( 4 | "errors" 5 | "fmt" 6 | "os" 7 | "strings" 8 | "text/template" 9 | 10 | "github.com/goreleaser/chglog" 11 | "github.com/spf13/cobra" 12 | "github.com/spf13/viper" 13 | ) 14 | 15 | // ErrTemplateFlags occurs when the user tries to use both --template and --template-file flags. 16 | var ErrTemplateFlags = errors.New("--template and --template-file are mutually exclusive") 17 | 18 | // nolint: funlen, gocritic 19 | func FormatCmd(config *viper.Viper) (cmd *cobra.Command) { 20 | var input, 21 | output, 22 | pkg, 23 | templateName, 24 | templateFile string 25 | cmd = &cobra.Command{ 26 | Use: "format", 27 | Short: "format entries to a specific template", 28 | } 29 | cmd.Flags().StringVarP( 30 | &output, 31 | "output", 32 | "o", 33 | "-", 34 | "file to save the output to (- is stdout)") 35 | cmd.Flags().StringVarP( 36 | &input, 37 | "input", 38 | "i", 39 | "changelog.yml", 40 | "changelog.yml file to use as the basis for formatting") 41 | cmd.Flags().StringVarP( 42 | &pkg, 43 | "package-name", 44 | "p", 45 | "", 46 | "package name to use in formatting") 47 | 48 | cmd.Flags().StringVarP( 49 | &templateName, 50 | "template", 51 | "t", 52 | "", 53 | "builtin template to use ('deb', 'rpm', 'release', 'repo')") 54 | cmd.Flags().StringVarP( 55 | &templateFile, 56 | "template-file", 57 | "", 58 | "", 59 | "custom template file to use") 60 | 61 | cmd.PreRunE = func(cmd *cobra.Command, _ []string) error { 62 | return config.BindPFlag("package-name", cmd.Flags().Lookup("package-name")) 63 | } 64 | 65 | cmd.RunE = func(*cobra.Command, []string) (err error) { 66 | var ( 67 | tpl *template.Template 68 | data []byte 69 | ret string 70 | fmtPackage = new(chglog.PackageChangeLog) 71 | ) 72 | if templateName != "" && templateFile != "" { 73 | return ErrTemplateFlags 74 | } 75 | 76 | switch strings.ToLower(templateName) { 77 | case "deb": 78 | tpl, err = chglog.DebTemplate() 79 | case "rpm": 80 | tpl, err = chglog.RPMTemplate() 81 | case "release": 82 | tpl, err = chglog.ReleaseTemplate() 83 | case "repo": 84 | tpl, err = chglog.RepoTemplate() 85 | default: 86 | // nolint: gosec, gocritic 87 | if data, err = os.ReadFile(templateFile); err != nil { 88 | return fmt.Errorf("error formatting entries: %w", err) 89 | } 90 | tpl, err = chglog.LoadTemplateData(string(data)) 91 | } 92 | if err != nil { 93 | return fmt.Errorf("error formatting entries: %w", err) 94 | } 95 | 96 | fmtPackage.Name = config.GetString("package-name") 97 | 98 | if fmtPackage.Entries, err = chglog.Parse(input); err != nil { 99 | return fmt.Errorf("error formatting entries: %w", err) 100 | } 101 | 102 | if ret, err = chglog.FormatChangelog(fmtPackage, tpl); err != nil { 103 | return fmt.Errorf("error formatting entries: %w", err) 104 | } 105 | 106 | if output == "-" { 107 | fmt.Println(ret) 108 | 109 | return 110 | } 111 | 112 | // nolint: gosec, gocritic 113 | return os.WriteFile(output, []byte(ret), 0o644) 114 | } 115 | 116 | return cmd 117 | } 118 | -------------------------------------------------------------------------------- /pkg/commands/init.go: -------------------------------------------------------------------------------- 1 | package commands 2 | 3 | import ( 4 | "errors" 5 | "fmt" 6 | "os" 7 | "path/filepath" 8 | 9 | "github.com/go-git/go-git/v5" 10 | "github.com/goreleaser/chglog" 11 | "github.com/spf13/cobra" 12 | "github.com/spf13/viper" 13 | ) 14 | 15 | // ErrNoTags happens when a repository has no tags. 16 | var ErrNoTags = errors.New("no versioned releases found, check the output of `git tag`") 17 | 18 | func InitCmd(config *viper.Viper) (cmd *cobra.Command) { 19 | var output string 20 | cmd = &cobra.Command{ 21 | Use: "init [PATH]", 22 | Short: "create a new changelog file for [PATH]", 23 | Args: cobra.MaximumNArgs(1), 24 | } 25 | 26 | cmd, config = commonFlags(cmd, config) 27 | cmd.Flags().StringVarP( 28 | &output, 29 | "output", 30 | "o", 31 | "changelog.yml", 32 | "file to save the new changelog to") 33 | 34 | cmd.RunE = func(_ *cobra.Command, args []string) (err error) { 35 | var ( 36 | repoPath string 37 | gitRepo *git.Repository 38 | entries chglog.ChangeLogEntries 39 | ) 40 | if repoPath, err = os.Getwd(); err != nil { 41 | return fmt.Errorf("error initialzing change log: %w", err) 42 | } 43 | 44 | if len(args) == 1 { 45 | if repoPath, err = filepath.Abs(args[0]); err != nil { 46 | return fmt.Errorf("error initialzing change log: %w", err) 47 | } 48 | } 49 | 50 | if gitRepo, err = chglog.GitRepo(repoPath, true); err != nil { 51 | return fmt.Errorf("error initialzing change log: %w", err) 52 | } 53 | 54 | if entries, err = chglog.InitChangelog(gitRepo, config.GetString("owner"), nil, getDeb(config), config.GetBool("conventional-commits"), config.GetBool("exclude-merge-commits")); err != nil { 55 | return fmt.Errorf("error initialzing change log: %w", err) 56 | } 57 | 58 | if len(entries) == 0 { 59 | return fmt.Errorf("%w: %s", ErrNoTags, repoPath) 60 | } 61 | 62 | if err := entries.Save(output); err != nil { 63 | return err 64 | } 65 | 66 | fmt.Println("created:", output) 67 | return nil 68 | } 69 | 70 | return cmd 71 | } 72 | -------------------------------------------------------------------------------- /pkg/commands/version.go: -------------------------------------------------------------------------------- 1 | package commands 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/spf13/cobra" 7 | "github.com/spf13/viper" 8 | ) 9 | 10 | func VersionCmd(config *viper.Viper) (cmd *cobra.Command) { 11 | cmd = &cobra.Command{ 12 | Use: "version", 13 | Short: "display version info", 14 | } 15 | cmd.Run = func(*cobra.Command, []string) { 16 | version := fmt.Sprintf("%s %s", config.GetString("app.name"), config.GetString("app.version")) 17 | if config.GetBool("debug") { 18 | version = fmt.Sprintf("%s+%s", version, config.GetString("app.commit")) 19 | } 20 | fmt.Println(version) 21 | } 22 | 23 | return cmd 24 | } 25 | -------------------------------------------------------------------------------- /templates.go: -------------------------------------------------------------------------------- 1 | package chglog 2 | 3 | import ( 4 | "text/template" 5 | 6 | "github.com/Masterminds/sprig/v3" 7 | ) 8 | 9 | const ( 10 | rpmTpl = ` 11 | {{- range .Entries }}{{$version := semver .Semver}} 12 | * {{ date_in_zone "Mon Jan 2 2006" .Date "UTC" }} {{ .Packager }} - {{ $version.Major }}.{{ $version.Minor }}.{{ $version.Patch }}{{if $version.Prerelease}}-{{ $version.Prerelease }}{{end}} 13 | {{- range .Changes }}{{$note := splitList "\n" .Note}} 14 | - {{ first $note }}{{ range $i,$n := (rest $note) }}{{if ne $n "\n"}} {{$n}}{{end}} 15 | {{end}} 16 | {{- end }} 17 | {{ end }} 18 | ` 19 | debTpl = `{{- $name := .Name}} 20 | {{- range .Entries }} 21 | {{ $name }} ({{ .Semver }}){{if .Deb}} {{default "" (.Deb.Distributions | join " ")}}; urgency={{default "low" .Deb.Urgency}}{{end}} 22 | {{- range .Changes }}{{$note := splitList "\n" .Note}} 23 | * {{ first $note }} 24 | {{- range $i,$n := (rest $note) }} 25 | {{- if ne (trim $n) ""}} 26 | - {{$n}}{{end}} 27 | {{- end}}{{end}} 28 | 29 | -- {{ .Packager }} {{ date_in_zone "Mon, 02 Jan 2006 15:04:05 -0700" .Date "UTC" }} 30 | {{ end }} 31 | ` 32 | releaseTpl = ` 33 | Changelog 34 | ========= 35 | {{- with (first .Entries)}} 36 | {{range .Changes }}{{$note := splitList "\n" .Note}} 37 | {{substr 0 8 .Commit}} {{ first $note }}{{end}} 38 | {{ end}} 39 | ` 40 | repoTpl = ` 41 | {{- range .Entries }} 42 | {{ .Semver }} 43 | ============= 44 | {{ date_in_zone "2006-01-02" .Date "UTC" }} 45 | {{range .Changes }}{{$note := splitList "\n" .Note}} 46 | * {{ first $note }} ({{substr 0 8 .Commit}}){{end}} 47 | {{ end}} 48 | ` 49 | ) 50 | 51 | // LoadTemplateData load a template from string with all of the sprig.TxtFuncMap loaded. 52 | func LoadTemplateData(data string) (*template.Template, error) { 53 | return template.New("base").Funcs(sprig.TxtFuncMap()).Parse(data) 54 | } 55 | 56 | // DebTemplate load default debian template. 57 | func DebTemplate() (*template.Template, error) { 58 | return LoadTemplateData(debTpl) 59 | } 60 | 61 | // RPMTemplate load default RPM template. 62 | func RPMTemplate() (*template.Template, error) { 63 | return LoadTemplateData(rpmTpl) 64 | } 65 | 66 | // ReleaseTemplate load default release template. 67 | func ReleaseTemplate() (*template.Template, error) { 68 | return LoadTemplateData(releaseTpl) 69 | } 70 | 71 | // RepoTemplate load default repo template. 72 | func RepoTemplate() (*template.Template, error) { 73 | return LoadTemplateData(repoTpl) 74 | } 75 | -------------------------------------------------------------------------------- /testdata/TestFormatChangelog-deb: -------------------------------------------------------------------------------- 1 | 2 | TestFormatChangelog-deb (0.0.1) 3 | * oops i forgot to use Conventional Commits style message 4 | - This should NOT break anything even if I am asking to build the changelog using Conventional Commits style message 5 | * feat: added file two feature 6 | - BREAKING CHANGE: this is a backwards incompatible change 7 | * feat: added the fileone feature 8 | - * This is a test repo 9 | - * so ya! 10 | 11 | -- Dj Gilcrease Fri, 18 Oct 2019 23:05:33 +0000 12 | 13 | -------------------------------------------------------------------------------- /testdata/TestFormatChangelog-release: -------------------------------------------------------------------------------- 1 | 2 | Changelog 3 | ========= 4 | 5 | 2c499787 oops i forgot to use Conventional Commits style message 6 | 3ec1e9a6 feat: added file two feature 7 | 2cc00abc feat: added the fileone feature 8 | 9 | -------------------------------------------------------------------------------- /testdata/TestFormatChangelog-repo: -------------------------------------------------------------------------------- 1 | 2 | 0.0.1 3 | ============= 4 | 2019-10-18 5 | 6 | * oops i forgot to use Conventional Commits style message (2c499787) 7 | * feat: added file two feature (3ec1e9a6) 8 | * feat: added the fileone feature (2cc00abc) 9 | 10 | -------------------------------------------------------------------------------- /testdata/TestFormatChangelog-rpm: -------------------------------------------------------------------------------- 1 | 2 | * Fri Oct 18 2019 Dj Gilcrease - 0.0.1 3 | - oops i forgot to use Conventional Commits style message 4 | This should NOT break anything even if I am asking to build the changelog using Conventional Commits style message 5 | 6 | - feat: added file two feature 7 | BREAKING CHANGE: this is a backwards incompatible change 8 | 9 | - feat: added the fileone feature 10 | * This is a test repo 11 | * so ya! 12 | 13 | 14 | -------------------------------------------------------------------------------- /testdata/add-repo-annotated-tag/HEAD: -------------------------------------------------------------------------------- 1 | ref: refs/heads/master 2 | -------------------------------------------------------------------------------- /testdata/add-repo-annotated-tag/config: -------------------------------------------------------------------------------- 1 | [core] 2 | repositoryformatversion = 0 3 | filemode = true 4 | bare = true 5 | -------------------------------------------------------------------------------- /testdata/add-repo-annotated-tag/description: -------------------------------------------------------------------------------- 1 | Unnamed repository; edit this file 'description' to name the repository. 2 | -------------------------------------------------------------------------------- /testdata/add-repo-annotated-tag/info/exclude: -------------------------------------------------------------------------------- 1 | # git ls-files --others --exclude-from=.git/info/exclude 2 | # Lines that start with '#' are comments. 3 | # For a project mostly in C, the following would be a good set of 4 | # exclude patterns (uncomment them if you want to use them): 5 | # *.[oa] 6 | # *~ 7 | -------------------------------------------------------------------------------- /testdata/add-repo-annotated-tag/objects/09/5b7b431351080c1edfe7de73db893e6b59ddce: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/goreleaser/chglog/b46d97bd57b12f8c0dc317dcd0568dff5db5b396/testdata/add-repo-annotated-tag/objects/09/5b7b431351080c1edfe7de73db893e6b59ddce -------------------------------------------------------------------------------- /testdata/add-repo-annotated-tag/objects/2c/499787328348f09ae1e8f03757c6483b9a938a: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/goreleaser/chglog/b46d97bd57b12f8c0dc317dcd0568dff5db5b396/testdata/add-repo-annotated-tag/objects/2c/499787328348f09ae1e8f03757c6483b9a938a -------------------------------------------------------------------------------- /testdata/add-repo-annotated-tag/objects/2c/c00abc77d401a541d18c26e5c7fbef1effd3ed: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/goreleaser/chglog/b46d97bd57b12f8c0dc317dcd0568dff5db5b396/testdata/add-repo-annotated-tag/objects/2c/c00abc77d401a541d18c26e5c7fbef1effd3ed -------------------------------------------------------------------------------- /testdata/add-repo-annotated-tag/objects/3e/c1e9a60d07cc060cee727c97ffc8aac5713943: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/goreleaser/chglog/b46d97bd57b12f8c0dc317dcd0568dff5db5b396/testdata/add-repo-annotated-tag/objects/3e/c1e9a60d07cc060cee727c97ffc8aac5713943 -------------------------------------------------------------------------------- /testdata/add-repo-annotated-tag/objects/43/3581c28b23426dee2c498da28defa9f5bd4f63: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/goreleaser/chglog/b46d97bd57b12f8c0dc317dcd0568dff5db5b396/testdata/add-repo-annotated-tag/objects/43/3581c28b23426dee2c498da28defa9f5bd4f63 -------------------------------------------------------------------------------- /testdata/add-repo-annotated-tag/objects/55/2682b593e1bea86425fd6d9520f2687b28487b: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/goreleaser/chglog/b46d97bd57b12f8c0dc317dcd0568dff5db5b396/testdata/add-repo-annotated-tag/objects/55/2682b593e1bea86425fd6d9520f2687b28487b -------------------------------------------------------------------------------- /testdata/add-repo-annotated-tag/objects/6f/59e5e770be445518775b22bbe25e89339b778b: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/goreleaser/chglog/b46d97bd57b12f8c0dc317dcd0568dff5db5b396/testdata/add-repo-annotated-tag/objects/6f/59e5e770be445518775b22bbe25e89339b778b -------------------------------------------------------------------------------- /testdata/add-repo-annotated-tag/objects/6f/6c178ef5f187bb7d4088a0415ac5a9ff0aef45: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/goreleaser/chglog/b46d97bd57b12f8c0dc317dcd0568dff5db5b396/testdata/add-repo-annotated-tag/objects/6f/6c178ef5f187bb7d4088a0415ac5a9ff0aef45 -------------------------------------------------------------------------------- /testdata/add-repo-annotated-tag/objects/ab/515204e62e6fd2b468736de7aadcaca4d9dffd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/goreleaser/chglog/b46d97bd57b12f8c0dc317dcd0568dff5db5b396/testdata/add-repo-annotated-tag/objects/ab/515204e62e6fd2b468736de7aadcaca4d9dffd -------------------------------------------------------------------------------- /testdata/add-repo-annotated-tag/objects/c2/b01f193085b4bc020ef62b93d9dc44d9eca7a4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/goreleaser/chglog/b46d97bd57b12f8c0dc317dcd0568dff5db5b396/testdata/add-repo-annotated-tag/objects/c2/b01f193085b4bc020ef62b93d9dc44d9eca7a4 -------------------------------------------------------------------------------- /testdata/add-repo-annotated-tag/objects/e6/9de29bb2d1d6434b8b29ae775ad8c2e48c5391: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/goreleaser/chglog/b46d97bd57b12f8c0dc317dcd0568dff5db5b396/testdata/add-repo-annotated-tag/objects/e6/9de29bb2d1d6434b8b29ae775ad8c2e48c5391 -------------------------------------------------------------------------------- /testdata/add-repo-annotated-tag/objects/ea/2b12e15f99419047f49d5fc5dc844073899da8: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/goreleaser/chglog/b46d97bd57b12f8c0dc317dcd0568dff5db5b396/testdata/add-repo-annotated-tag/objects/ea/2b12e15f99419047f49d5fc5dc844073899da8 -------------------------------------------------------------------------------- /testdata/add-repo-annotated-tag/refs/heads/master: -------------------------------------------------------------------------------- 1 | 095b7b431351080c1edfe7de73db893e6b59ddce 2 | -------------------------------------------------------------------------------- /testdata/add-repo-annotated-tag/refs/tags/v0.0.1: -------------------------------------------------------------------------------- 1 | 2c499787328348f09ae1e8f03757c6483b9a938a 2 | -------------------------------------------------------------------------------- /testdata/add-repo-annotated-tag/refs/tags/v0.0.2: -------------------------------------------------------------------------------- 1 | 552682b593e1bea86425fd6d9520f2687b28487b 2 | -------------------------------------------------------------------------------- /testdata/add-repo/HEAD: -------------------------------------------------------------------------------- 1 | ref: refs/heads/master 2 | -------------------------------------------------------------------------------- /testdata/add-repo/config: -------------------------------------------------------------------------------- 1 | [core] 2 | repositoryformatversion = 0 3 | filemode = true 4 | bare = true 5 | -------------------------------------------------------------------------------- /testdata/add-repo/description: -------------------------------------------------------------------------------- 1 | Unnamed repository; edit this file 'description' to name the repository. 2 | -------------------------------------------------------------------------------- /testdata/add-repo/hooks/applypatch-msg.sample: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # 3 | # An example hook script to check the commit log message taken by 4 | # applypatch from an e-mail message. 5 | # 6 | # The hook should exit with non-zero status after issuing an 7 | # appropriate message if it wants to stop the commit. The hook is 8 | # allowed to edit the commit message file. 9 | # 10 | # To enable this hook, rename this file to "applypatch-msg". 11 | 12 | . git-sh-setup 13 | commitmsg="$(git rev-parse --git-path hooks/commit-msg)" 14 | test -x "$commitmsg" && exec "$commitmsg" ${1+"$@"} 15 | : 16 | -------------------------------------------------------------------------------- /testdata/add-repo/hooks/commit-msg.sample: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # 3 | # An example hook script to check the commit log message. 4 | # Called by "git commit" with one argument, the name of the file 5 | # that has the commit message. The hook should exit with non-zero 6 | # status after issuing an appropriate message if it wants to stop the 7 | # commit. The hook is allowed to edit the commit message file. 8 | # 9 | # To enable this hook, rename this file to "commit-msg". 10 | 11 | # Uncomment the below to add a Signed-off-by line to the message. 12 | # Doing this in a hook is a bad idea in general, but the prepare-commit-msg 13 | # hook is more suited to it. 14 | # 15 | # SOB=$(git var GIT_AUTHOR_IDENT | sed -n 's/^\(.*>\).*$/Signed-off-by: \1/p') 16 | # grep -qs "^$SOB" "$1" || echo "$SOB" >> "$1" 17 | 18 | # This example catches duplicate Signed-off-by lines. 19 | 20 | test "" = "$(grep '^Signed-off-by: ' "$1" | 21 | sort | uniq -c | sed -e '/^[ ]*1[ ]/d')" || { 22 | echo >&2 Duplicate Signed-off-by lines. 23 | exit 1 24 | } 25 | -------------------------------------------------------------------------------- /testdata/add-repo/hooks/fsmonitor-watchman.sample: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl 2 | 3 | use strict; 4 | use warnings; 5 | use IPC::Open2; 6 | 7 | # An example hook script to integrate Watchman 8 | # (https://facebook.github.io/watchman/) with git to speed up detecting 9 | # new and modified files. 10 | # 11 | # The hook is passed a version (currently 1) and a time in nanoseconds 12 | # formatted as a string and outputs to stdout all files that have been 13 | # modified since the given time. Paths must be relative to the root of 14 | # the working tree and separated by a single NUL. 15 | # 16 | # To enable this hook, rename this file to "query-watchman" and set 17 | # 'git config core.fsmonitor .git/hooks/query-watchman' 18 | # 19 | my ($version, $time) = @ARGV; 20 | 21 | # Check the hook interface version 22 | 23 | if ($version == 1) { 24 | # convert nanoseconds to seconds 25 | $time = int $time / 1000000000; 26 | } else { 27 | die "Unsupported query-fsmonitor hook version '$version'.\n" . 28 | "Falling back to scanning...\n"; 29 | } 30 | 31 | my $git_work_tree; 32 | if ($^O =~ 'msys' || $^O =~ 'cygwin') { 33 | $git_work_tree = Win32::GetCwd(); 34 | $git_work_tree =~ tr/\\/\//; 35 | } else { 36 | require Cwd; 37 | $git_work_tree = Cwd::cwd(); 38 | } 39 | 40 | my $retry = 1; 41 | 42 | launch_watchman(); 43 | 44 | sub launch_watchman { 45 | 46 | my $pid = open2(\*CHLD_OUT, \*CHLD_IN, 'watchman -j --no-pretty') 47 | or die "open2() failed: $!\n" . 48 | "Falling back to scanning...\n"; 49 | 50 | # In the query expression below we're asking for names of files that 51 | # changed since $time but were not transient (ie created after 52 | # $time but no longer exist). 53 | # 54 | # To accomplish this, we're using the "since" generator to use the 55 | # recency index to select candidate nodes and "fields" to limit the 56 | # output to file names only. Then we're using the "expression" term to 57 | # further constrain the results. 58 | # 59 | # The category of transient files that we want to ignore will have a 60 | # creation clock (cclock) newer than $time_t value and will also not 61 | # currently exist. 62 | 63 | my $query = <<" END"; 64 | ["query", "$git_work_tree", { 65 | "since": $time, 66 | "fields": ["name"], 67 | "expression": ["not", ["allof", ["since", $time, "cclock"], ["not", "exists"]]] 68 | }] 69 | END 70 | 71 | print CHLD_IN $query; 72 | close CHLD_IN; 73 | my $response = do {local $/; }; 74 | 75 | die "Watchman: command returned no output.\n" . 76 | "Falling back to scanning...\n" if $response eq ""; 77 | die "Watchman: command returned invalid output: $response\n" . 78 | "Falling back to scanning...\n" unless $response =~ /^\{/; 79 | 80 | my $json_pkg; 81 | eval { 82 | require JSON::XS; 83 | $json_pkg = "JSON::XS"; 84 | 1; 85 | } or do { 86 | require JSON::PP; 87 | $json_pkg = "JSON::PP"; 88 | }; 89 | 90 | my $o = $json_pkg->new->utf8->decode($response); 91 | 92 | if ($retry > 0 and $o->{error} and $o->{error} =~ m/unable to resolve root .* directory (.*) is not watched/) { 93 | print STDERR "Adding '$git_work_tree' to watchman's watch list.\n"; 94 | $retry--; 95 | qx/watchman watch "$git_work_tree"/; 96 | die "Failed to make watchman watch '$git_work_tree'.\n" . 97 | "Falling back to scanning...\n" if $? != 0; 98 | 99 | # Watchman will always return all files on the first query so 100 | # return the fast "everything is dirty" flag to git and do the 101 | # Watchman query just to get it over with now so we won't pay 102 | # the cost in git to look up each individual file. 103 | print "/\0"; 104 | eval { launch_watchman() }; 105 | exit 0; 106 | } 107 | 108 | die "Watchman: $o->{error}.\n" . 109 | "Falling back to scanning...\n" if $o->{error}; 110 | 111 | binmode STDOUT, ":utf8"; 112 | local $, = "\0"; 113 | print @{$o->{files}}; 114 | } 115 | -------------------------------------------------------------------------------- /testdata/add-repo/hooks/post-update.sample: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # 3 | # An example hook script to prepare a packed repository for use over 4 | # dumb transports. 5 | # 6 | # To enable this hook, rename this file to "post-update". 7 | 8 | exec git update-server-info 9 | -------------------------------------------------------------------------------- /testdata/add-repo/hooks/pre-applypatch.sample: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # 3 | # An example hook script to verify what is about to be committed 4 | # by applypatch from an e-mail message. 5 | # 6 | # The hook should exit with non-zero status after issuing an 7 | # appropriate message if it wants to stop the commit. 8 | # 9 | # To enable this hook, rename this file to "pre-applypatch". 10 | 11 | . git-sh-setup 12 | precommit="$(git rev-parse --git-path hooks/pre-commit)" 13 | test -x "$precommit" && exec "$precommit" ${1+"$@"} 14 | : 15 | -------------------------------------------------------------------------------- /testdata/add-repo/hooks/pre-commit.sample: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # 3 | # An example hook script to verify what is about to be committed. 4 | # Called by "git commit" with no arguments. The hook should 5 | # exit with non-zero status after issuing an appropriate message if 6 | # it wants to stop the commit. 7 | # 8 | # To enable this hook, rename this file to "pre-commit". 9 | 10 | if git rev-parse --verify HEAD >/dev/null 2>&1 11 | then 12 | against=HEAD 13 | else 14 | # Initial commit: diff against an empty tree object 15 | against=$(git hash-object -t tree /dev/null) 16 | fi 17 | 18 | # If you want to allow non-ASCII filenames set this variable to true. 19 | allownonascii=$(git config --bool hooks.allownonascii) 20 | 21 | # Redirect output to stderr. 22 | exec 1>&2 23 | 24 | # Cross platform projects tend to avoid non-ASCII filenames; prevent 25 | # them from being added to the repository. We exploit the fact that the 26 | # printable range starts at the space character and ends with tilde. 27 | if [ "$allownonascii" != "true" ] && 28 | # Note that the use of brackets around a tr range is ok here, (it's 29 | # even required, for portability to Solaris 10's /usr/bin/tr), since 30 | # the square bracket bytes happen to fall in the designated range. 31 | test $(git diff --cached --name-only --diff-filter=A -z $against | 32 | LC_ALL=C tr -d '[ -~]\0' | wc -c) != 0 33 | then 34 | cat <<\EOF 35 | Error: Attempt to add a non-ASCII file name. 36 | 37 | This can cause problems if you want to work with people on other platforms. 38 | 39 | To be portable it is advisable to rename the file. 40 | 41 | If you know what you are doing you can disable this check using: 42 | 43 | git config hooks.allownonascii true 44 | EOF 45 | exit 1 46 | fi 47 | 48 | # If there are whitespace errors, print the offending file names and fail. 49 | exec git diff-index --check --cached $against -- 50 | -------------------------------------------------------------------------------- /testdata/add-repo/hooks/pre-push.sample: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # An example hook script to verify what is about to be pushed. Called by "git 4 | # push" after it has checked the remote status, but before anything has been 5 | # pushed. If this script exits with a non-zero status nothing will be pushed. 6 | # 7 | # This hook is called with the following parameters: 8 | # 9 | # $1 -- Name of the remote to which the push is being done 10 | # $2 -- URL to which the push is being done 11 | # 12 | # If pushing without using a named remote those arguments will be equal. 13 | # 14 | # Information about the commits which are being pushed is supplied as lines to 15 | # the standard input in the form: 16 | # 17 | # 18 | # 19 | # This sample shows how to prevent push of commits where the log message starts 20 | # with "WIP" (work in progress). 21 | 22 | remote="$1" 23 | url="$2" 24 | 25 | z40=0000000000000000000000000000000000000000 26 | 27 | while read local_ref local_sha remote_ref remote_sha 28 | do 29 | if [ "$local_sha" = $z40 ] 30 | then 31 | # Handle delete 32 | : 33 | else 34 | if [ "$remote_sha" = $z40 ] 35 | then 36 | # New branch, examine all commits 37 | range="$local_sha" 38 | else 39 | # Update to existing branch, examine new commits 40 | range="$remote_sha..$local_sha" 41 | fi 42 | 43 | # Check for WIP commit 44 | commit=`git rev-list -n 1 --grep '^WIP' "$range"` 45 | if [ -n "$commit" ] 46 | then 47 | echo >&2 "Found WIP commit in $local_ref, not pushing" 48 | exit 1 49 | fi 50 | fi 51 | done 52 | 53 | exit 0 54 | -------------------------------------------------------------------------------- /testdata/add-repo/hooks/pre-rebase.sample: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # 3 | # Copyright (c) 2006, 2008 Junio C Hamano 4 | # 5 | # The "pre-rebase" hook is run just before "git rebase" starts doing 6 | # its job, and can prevent the command from running by exiting with 7 | # non-zero status. 8 | # 9 | # The hook is called with the following parameters: 10 | # 11 | # $1 -- the upstream the series was forked from. 12 | # $2 -- the branch being rebased (or empty when rebasing the current branch). 13 | # 14 | # This sample shows how to prevent topic branches that are already 15 | # merged to 'next' branch from getting rebased, because allowing it 16 | # would result in rebasing already published history. 17 | 18 | publish=next 19 | basebranch="$1" 20 | if test "$#" = 2 21 | then 22 | topic="refs/heads/$2" 23 | else 24 | topic=`git symbolic-ref HEAD` || 25 | exit 0 ;# we do not interrupt rebasing detached HEAD 26 | fi 27 | 28 | case "$topic" in 29 | refs/heads/??/*) 30 | ;; 31 | *) 32 | exit 0 ;# we do not interrupt others. 33 | ;; 34 | esac 35 | 36 | # Now we are dealing with a topic branch being rebased 37 | # on top of master. Is it OK to rebase it? 38 | 39 | # Does the topic really exist? 40 | git show-ref -q "$topic" || { 41 | echo >&2 "No such branch $topic" 42 | exit 1 43 | } 44 | 45 | # Is topic fully merged to master? 46 | not_in_master=`git rev-list --pretty=oneline ^master "$topic"` 47 | if test -z "$not_in_master" 48 | then 49 | echo >&2 "$topic is fully merged to master; better remove it." 50 | exit 1 ;# we could allow it, but there is no point. 51 | fi 52 | 53 | # Is topic ever merged to next? If so you should not be rebasing it. 54 | only_next_1=`git rev-list ^master "^$topic" ${publish} | sort` 55 | only_next_2=`git rev-list ^master ${publish} | sort` 56 | if test "$only_next_1" = "$only_next_2" 57 | then 58 | not_in_topic=`git rev-list "^$topic" master` 59 | if test -z "$not_in_topic" 60 | then 61 | echo >&2 "$topic is already up to date with master" 62 | exit 1 ;# we could allow it, but there is no point. 63 | else 64 | exit 0 65 | fi 66 | else 67 | not_in_next=`git rev-list --pretty=oneline ^${publish} "$topic"` 68 | /usr/bin/perl -e ' 69 | my $topic = $ARGV[0]; 70 | my $msg = "* $topic has commits already merged to public branch:\n"; 71 | my (%not_in_next) = map { 72 | /^([0-9a-f]+) /; 73 | ($1 => 1); 74 | } split(/\n/, $ARGV[1]); 75 | for my $elem (map { 76 | /^([0-9a-f]+) (.*)$/; 77 | [$1 => $2]; 78 | } split(/\n/, $ARGV[2])) { 79 | if (!exists $not_in_next{$elem->[0]}) { 80 | if ($msg) { 81 | print STDERR $msg; 82 | undef $msg; 83 | } 84 | print STDERR " $elem->[1]\n"; 85 | } 86 | } 87 | ' "$topic" "$not_in_next" "$not_in_master" 88 | exit 1 89 | fi 90 | 91 | <<\DOC_END 92 | 93 | This sample hook safeguards topic branches that have been 94 | published from being rewound. 95 | 96 | The workflow assumed here is: 97 | 98 | * Once a topic branch forks from "master", "master" is never 99 | merged into it again (either directly or indirectly). 100 | 101 | * Once a topic branch is fully cooked and merged into "master", 102 | it is deleted. If you need to build on top of it to correct 103 | earlier mistakes, a new topic branch is created by forking at 104 | the tip of the "master". This is not strictly necessary, but 105 | it makes it easier to keep your history simple. 106 | 107 | * Whenever you need to test or publish your changes to topic 108 | branches, merge them into "next" branch. 109 | 110 | The script, being an example, hardcodes the publish branch name 111 | to be "next", but it is trivial to make it configurable via 112 | $GIT_DIR/config mechanism. 113 | 114 | With this workflow, you would want to know: 115 | 116 | (1) ... if a topic branch has ever been merged to "next". Young 117 | topic branches can have stupid mistakes you would rather 118 | clean up before publishing, and things that have not been 119 | merged into other branches can be easily rebased without 120 | affecting other people. But once it is published, you would 121 | not want to rewind it. 122 | 123 | (2) ... if a topic branch has been fully merged to "master". 124 | Then you can delete it. More importantly, you should not 125 | build on top of it -- other people may already want to 126 | change things related to the topic as patches against your 127 | "master", so if you need further changes, it is better to 128 | fork the topic (perhaps with the same name) afresh from the 129 | tip of "master". 130 | 131 | Let's look at this example: 132 | 133 | o---o---o---o---o---o---o---o---o---o "next" 134 | / / / / 135 | / a---a---b A / / 136 | / / / / 137 | / / c---c---c---c B / 138 | / / / \ / 139 | / / / b---b C \ / 140 | / / / / \ / 141 | ---o---o---o---o---o---o---o---o---o---o---o "master" 142 | 143 | 144 | A, B and C are topic branches. 145 | 146 | * A has one fix since it was merged up to "next". 147 | 148 | * B has finished. It has been fully merged up to "master" and "next", 149 | and is ready to be deleted. 150 | 151 | * C has not merged to "next" at all. 152 | 153 | We would want to allow C to be rebased, refuse A, and encourage 154 | B to be deleted. 155 | 156 | To compute (1): 157 | 158 | git rev-list ^master ^topic next 159 | git rev-list ^master next 160 | 161 | if these match, topic has not merged in next at all. 162 | 163 | To compute (2): 164 | 165 | git rev-list master..topic 166 | 167 | if this is empty, it is fully merged to "master". 168 | 169 | DOC_END 170 | -------------------------------------------------------------------------------- /testdata/add-repo/hooks/pre-receive.sample: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # 3 | # An example hook script to make use of push options. 4 | # The example simply echoes all push options that start with 'echoback=' 5 | # and rejects all pushes when the "reject" push option is used. 6 | # 7 | # To enable this hook, rename this file to "pre-receive". 8 | 9 | if test -n "$GIT_PUSH_OPTION_COUNT" 10 | then 11 | i=0 12 | while test "$i" -lt "$GIT_PUSH_OPTION_COUNT" 13 | do 14 | eval "value=\$GIT_PUSH_OPTION_$i" 15 | case "$value" in 16 | echoback=*) 17 | echo "echo from the pre-receive-hook: ${value#*=}" >&2 18 | ;; 19 | reject) 20 | exit 1 21 | esac 22 | i=$((i + 1)) 23 | done 24 | fi 25 | -------------------------------------------------------------------------------- /testdata/add-repo/hooks/prepare-commit-msg.sample: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # 3 | # An example hook script to prepare the commit log message. 4 | # Called by "git commit" with the name of the file that has the 5 | # commit message, followed by the description of the commit 6 | # message's source. The hook's purpose is to edit the commit 7 | # message file. If the hook fails with a non-zero status, 8 | # the commit is aborted. 9 | # 10 | # To enable this hook, rename this file to "prepare-commit-msg". 11 | 12 | # This hook includes three examples. The first one removes the 13 | # "# Please enter the commit message..." help message. 14 | # 15 | # The second includes the output of "git diff --name-status -r" 16 | # into the message, just before the "git status" output. It is 17 | # commented because it doesn't cope with --amend or with squashed 18 | # commits. 19 | # 20 | # The third example adds a Signed-off-by line to the message, that can 21 | # still be edited. This is rarely a good idea. 22 | 23 | COMMIT_MSG_FILE=$1 24 | COMMIT_SOURCE=$2 25 | SHA1=$3 26 | 27 | /usr/bin/perl -i.bak -ne 'print unless(m/^. Please enter the commit message/..m/^#$/)' "$COMMIT_MSG_FILE" 28 | 29 | # case "$COMMIT_SOURCE,$SHA1" in 30 | # ,|template,) 31 | # /usr/bin/perl -i.bak -pe ' 32 | # print "\n" . `git diff --cached --name-status -r` 33 | # if /^#/ && $first++ == 0' "$COMMIT_MSG_FILE" ;; 34 | # *) ;; 35 | # esac 36 | 37 | # SOB=$(git var GIT_COMMITTER_IDENT | sed -n 's/^\(.*>\).*$/Signed-off-by: \1/p') 38 | # git interpret-trailers --in-place --trailer "$SOB" "$COMMIT_MSG_FILE" 39 | # if test -z "$COMMIT_SOURCE" 40 | # then 41 | # /usr/bin/perl -i.bak -pe 'print "\n" if !$first_line++' "$COMMIT_MSG_FILE" 42 | # fi 43 | -------------------------------------------------------------------------------- /testdata/add-repo/hooks/update.sample: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # 3 | # An example hook script to block unannotated tags from entering. 4 | # Called by "git receive-pack" with arguments: refname sha1-old sha1-new 5 | # 6 | # To enable this hook, rename this file to "update". 7 | # 8 | # Config 9 | # ------ 10 | # hooks.allowunannotated 11 | # This boolean sets whether unannotated tags will be allowed into the 12 | # repository. By default they won't be. 13 | # hooks.allowdeletetag 14 | # This boolean sets whether deleting tags will be allowed in the 15 | # repository. By default they won't be. 16 | # hooks.allowmodifytag 17 | # This boolean sets whether a tag may be modified after creation. By default 18 | # it won't be. 19 | # hooks.allowdeletebranch 20 | # This boolean sets whether deleting branches will be allowed in the 21 | # repository. By default they won't be. 22 | # hooks.denycreatebranch 23 | # This boolean sets whether remotely creating branches will be denied 24 | # in the repository. By default this is allowed. 25 | # 26 | 27 | # --- Command line 28 | refname="$1" 29 | oldrev="$2" 30 | newrev="$3" 31 | 32 | # --- Safety check 33 | if [ -z "$GIT_DIR" ]; then 34 | echo "Don't run this script from the command line." >&2 35 | echo " (if you want, you could supply GIT_DIR then run" >&2 36 | echo " $0 )" >&2 37 | exit 1 38 | fi 39 | 40 | if [ -z "$refname" -o -z "$oldrev" -o -z "$newrev" ]; then 41 | echo "usage: $0 " >&2 42 | exit 1 43 | fi 44 | 45 | # --- Config 46 | allowunannotated=$(git config --bool hooks.allowunannotated) 47 | allowdeletebranch=$(git config --bool hooks.allowdeletebranch) 48 | denycreatebranch=$(git config --bool hooks.denycreatebranch) 49 | allowdeletetag=$(git config --bool hooks.allowdeletetag) 50 | allowmodifytag=$(git config --bool hooks.allowmodifytag) 51 | 52 | # check for no description 53 | projectdesc=$(sed -e '1q' "$GIT_DIR/description") 54 | case "$projectdesc" in 55 | "Unnamed repository"* | "") 56 | echo "*** Project description file hasn't been set" >&2 57 | exit 1 58 | ;; 59 | esac 60 | 61 | # --- Check types 62 | # if $newrev is 0000...0000, it's a commit to delete a ref. 63 | zero="0000000000000000000000000000000000000000" 64 | if [ "$newrev" = "$zero" ]; then 65 | newrev_type=delete 66 | else 67 | newrev_type=$(git cat-file -t $newrev) 68 | fi 69 | 70 | case "$refname","$newrev_type" in 71 | refs/tags/*,commit) 72 | # un-annotated tag 73 | short_refname=${refname##refs/tags/} 74 | if [ "$allowunannotated" != "true" ]; then 75 | echo "*** The un-annotated tag, $short_refname, is not allowed in this repository" >&2 76 | echo "*** Use 'git tag [ -a | -s ]' for tags you want to propagate." >&2 77 | exit 1 78 | fi 79 | ;; 80 | refs/tags/*,delete) 81 | # delete tag 82 | if [ "$allowdeletetag" != "true" ]; then 83 | echo "*** Deleting a tag is not allowed in this repository" >&2 84 | exit 1 85 | fi 86 | ;; 87 | refs/tags/*,tag) 88 | # annotated tag 89 | if [ "$allowmodifytag" != "true" ] && git rev-parse $refname > /dev/null 2>&1 90 | then 91 | echo "*** Tag '$refname' already exists." >&2 92 | echo "*** Modifying a tag is not allowed in this repository." >&2 93 | exit 1 94 | fi 95 | ;; 96 | refs/heads/*,commit) 97 | # branch 98 | if [ "$oldrev" = "$zero" -a "$denycreatebranch" = "true" ]; then 99 | echo "*** Creating a branch is not allowed in this repository" >&2 100 | exit 1 101 | fi 102 | ;; 103 | refs/heads/*,delete) 104 | # delete branch 105 | if [ "$allowdeletebranch" != "true" ]; then 106 | echo "*** Deleting a branch is not allowed in this repository" >&2 107 | exit 1 108 | fi 109 | ;; 110 | refs/remotes/*,commit) 111 | # tracking branch 112 | ;; 113 | refs/remotes/*,delete) 114 | # delete tracking branch 115 | if [ "$allowdeletebranch" != "true" ]; then 116 | echo "*** Deleting a tracking branch is not allowed in this repository" >&2 117 | exit 1 118 | fi 119 | ;; 120 | *) 121 | # Anything else (is there anything else?) 122 | echo "*** Update hook: unknown type of update to ref $refname of type $newrev_type" >&2 123 | exit 1 124 | ;; 125 | esac 126 | 127 | # --- Finished 128 | exit 0 129 | -------------------------------------------------------------------------------- /testdata/add-repo/info/exclude: -------------------------------------------------------------------------------- 1 | # git ls-files --others --exclude-from=.git/info/exclude 2 | # Lines that start with '#' are comments. 3 | # For a project mostly in C, the following would be a good set of 4 | # exclude patterns (uncomment them if you want to use them): 5 | # *.[oa] 6 | # *~ 7 | -------------------------------------------------------------------------------- /testdata/add-repo/objects/2c/499787328348f09ae1e8f03757c6483b9a938a: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/goreleaser/chglog/b46d97bd57b12f8c0dc317dcd0568dff5db5b396/testdata/add-repo/objects/2c/499787328348f09ae1e8f03757c6483b9a938a -------------------------------------------------------------------------------- /testdata/add-repo/objects/2c/c00abc77d401a541d18c26e5c7fbef1effd3ed: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/goreleaser/chglog/b46d97bd57b12f8c0dc317dcd0568dff5db5b396/testdata/add-repo/objects/2c/c00abc77d401a541d18c26e5c7fbef1effd3ed -------------------------------------------------------------------------------- /testdata/add-repo/objects/3e/c1e9a60d07cc060cee727c97ffc8aac5713943: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/goreleaser/chglog/b46d97bd57b12f8c0dc317dcd0568dff5db5b396/testdata/add-repo/objects/3e/c1e9a60d07cc060cee727c97ffc8aac5713943 -------------------------------------------------------------------------------- /testdata/add-repo/objects/43/3581c28b23426dee2c498da28defa9f5bd4f63: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/goreleaser/chglog/b46d97bd57b12f8c0dc317dcd0568dff5db5b396/testdata/add-repo/objects/43/3581c28b23426dee2c498da28defa9f5bd4f63 -------------------------------------------------------------------------------- /testdata/add-repo/objects/6f/59e5e770be445518775b22bbe25e89339b778b: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/goreleaser/chglog/b46d97bd57b12f8c0dc317dcd0568dff5db5b396/testdata/add-repo/objects/6f/59e5e770be445518775b22bbe25e89339b778b -------------------------------------------------------------------------------- /testdata/add-repo/objects/ab/515204e62e6fd2b468736de7aadcaca4d9dffd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/goreleaser/chglog/b46d97bd57b12f8c0dc317dcd0568dff5db5b396/testdata/add-repo/objects/ab/515204e62e6fd2b468736de7aadcaca4d9dffd -------------------------------------------------------------------------------- /testdata/add-repo/objects/c2/b01f193085b4bc020ef62b93d9dc44d9eca7a4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/goreleaser/chglog/b46d97bd57b12f8c0dc317dcd0568dff5db5b396/testdata/add-repo/objects/c2/b01f193085b4bc020ef62b93d9dc44d9eca7a4 -------------------------------------------------------------------------------- /testdata/add-repo/objects/e6/9de29bb2d1d6434b8b29ae775ad8c2e48c5391: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/goreleaser/chglog/b46d97bd57b12f8c0dc317dcd0568dff5db5b396/testdata/add-repo/objects/e6/9de29bb2d1d6434b8b29ae775ad8c2e48c5391 -------------------------------------------------------------------------------- /testdata/add-repo/objects/ea/2b12e15f99419047f49d5fc5dc844073899da8: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/goreleaser/chglog/b46d97bd57b12f8c0dc317dcd0568dff5db5b396/testdata/add-repo/objects/ea/2b12e15f99419047f49d5fc5dc844073899da8 -------------------------------------------------------------------------------- /testdata/add-repo/refs/heads/master: -------------------------------------------------------------------------------- 1 | 433581c28b23426dee2c498da28defa9f5bd4f63 2 | -------------------------------------------------------------------------------- /testdata/add-repo/refs/tags/v0.0.1: -------------------------------------------------------------------------------- 1 | 2c499787328348f09ae1e8f03757c6483b9a938a 2 | -------------------------------------------------------------------------------- /testdata/gold-add-changelog-with-annotated-commit.yml: -------------------------------------------------------------------------------- 1 | - semver: 1.0.0-b1+git.123 2 | date: 2019-10-18T18:17:57.934767812-07:00 3 | packager: Dj Gilcrease 4 | notes: 5 | header: |2 6 | 7 | This is a test 8 | ====== 9 | 10 | header entry 11 | changes: 12 | - commit: 095b7b431351080c1edfe7de73db893e6b59ddce 13 | note: "chore: add another file" 14 | author: 15 | name: Nicholas Jackson 16 | email: nicholas.jackson@zii.aero 17 | committer: 18 | name: Nicholas Jackson 19 | email: nicholas.jackson@zii.aero 20 | conventional_commit: 21 | category: chore 22 | scope: "" 23 | description: add another file 24 | body: "" 25 | - semver: 0.0.2 26 | date: 2019-10-18T18:53:50-07:00 27 | packager: Dj Gilcrease 28 | changes: 29 | - commit: 433581c28b23426dee2c498da28defa9f5bd4f63 30 | note: "chore: add a new file" 31 | author: 32 | name: Dj Gilcrease 33 | email: d.gilcrease@f5.com 34 | committer: 35 | name: Dj Gilcrease 36 | email: d.gilcrease@f5.com 37 | conventional_commit: 38 | category: chore 39 | scope: "" 40 | description: add a new file 41 | body: "" 42 | - semver: 0.0.1 43 | date: 2019-10-18T16:05:33-07:00 44 | packager: Dj Gilcrease 45 | changes: 46 | - commit: 2c499787328348f09ae1e8f03757c6483b9a938a 47 | note: |- 48 | oops i forgot to use Conventional Commits style message 49 | 50 | This should NOT break anything even if I am asking to build the changelog using Conventional Commits style message 51 | author: 52 | name: Dj Gilcrease 53 | email: d.gilcrease@f5.com 54 | committer: 55 | name: Dj Gilcrease 56 | email: d.gilcrease@f5.com 57 | conventional_commit: 58 | category: chore 59 | scope: "" 60 | description: oops i forgot to use Conventional Commits style message 61 | body: This should NOT break anything even if I am asking to build the changelog using Conventional Commits style message 62 | - commit: 3ec1e9a60d07cc060cee727c97ffc8aac5713943 63 | note: |- 64 | feat: added file two feature 65 | 66 | BREAKING CHANGE: this is a backwards incompatible change 67 | author: 68 | name: Dj Gilcrease 69 | email: d.gilcrease@f5.com 70 | committer: 71 | name: Dj Gilcrease 72 | email: d.gilcrease@f5.com 73 | conventional_commit: 74 | category: feat 75 | scope: "" 76 | description: added file two feature 77 | body: "" 78 | footer: 79 | - "BREAKING CHANGE: this is a backwards incompatible change" 80 | major: true 81 | - commit: 2cc00abc77d401a541d18c26e5c7fbef1effd3ed 82 | note: |- 83 | feat: added the fileone feature 84 | 85 | * This is a test repo 86 | * so ya! 87 | author: 88 | name: Dj Gilcrease 89 | email: d.gilcrease@f5.com 90 | committer: 91 | name: Dj Gilcrease 92 | email: d.gilcrease@f5.com 93 | conventional_commit: 94 | category: feat 95 | scope: "" 96 | description: added the fileone feature 97 | body: |- 98 | * This is a test repo 99 | * so ya! 100 | minor: true 101 | -------------------------------------------------------------------------------- /testdata/gold-add-changelog.yml: -------------------------------------------------------------------------------- 1 | - semver: 1.0.0-b1+git.123 2 | date: 2019-10-18T18:17:57.934767812-07:00 3 | packager: Dj Gilcrease 4 | notes: 5 | header: |2 6 | 7 | This is a test 8 | ====== 9 | 10 | header entry 11 | changes: 12 | - commit: 433581c28b23426dee2c498da28defa9f5bd4f63 13 | note: 'chore: add a new file' 14 | author: 15 | name: Dj Gilcrease 16 | email: d.gilcrease@f5.com 17 | committer: 18 | name: Dj Gilcrease 19 | email: d.gilcrease@f5.com 20 | conventional_commit: 21 | category: chore 22 | scope: "" 23 | breaking: false 24 | description: add a new file 25 | body: "" 26 | - semver: 0.0.1 27 | date: 2019-10-18T16:05:33-07:00 28 | packager: Dj Gilcrease 29 | changes: 30 | - commit: 2c499787328348f09ae1e8f03757c6483b9a938a 31 | note: |- 32 | oops i forgot to use Conventional Commits style message 33 | 34 | This should NOT break anything even if I am asking to build the changelog using Conventional Commits style message 35 | author: 36 | name: Dj Gilcrease 37 | email: d.gilcrease@f5.com 38 | committer: 39 | name: Dj Gilcrease 40 | email: d.gilcrease@f5.com 41 | conventional_commit: 42 | category: "chore" 43 | scope: "" 44 | breaking: false 45 | description: oops i forgot to use Conventional Commits style message 46 | body: This should NOT break anything even if I am asking to build the changelog 47 | using Conventional Commits style message 48 | - commit: 3ec1e9a60d07cc060cee727c97ffc8aac5713943 49 | note: |- 50 | feat: added file two feature 51 | 52 | BREAKING CHANGE: this is a backwards incompatible change 53 | author: 54 | name: Dj Gilcrease 55 | email: d.gilcrease@f5.com 56 | committer: 57 | name: Dj Gilcrease 58 | email: d.gilcrease@f5.com 59 | conventional_commit: 60 | category: feat 61 | scope: "" 62 | breaking: true 63 | description: added file two feature 64 | major: true 65 | footer: 66 | - 'BREAKING CHANGE: this is a backwards incompatible change' 67 | - commit: 2cc00abc77d401a541d18c26e5c7fbef1effd3ed 68 | note: |- 69 | feat: added the fileone feature 70 | 71 | * This is a test repo 72 | * so ya! 73 | author: 74 | name: Dj Gilcrease 75 | email: d.gilcrease@f5.com 76 | committer: 77 | name: Dj Gilcrease 78 | email: d.gilcrease@f5.com 79 | conventional_commit: 80 | category: feat 81 | scope: "" 82 | breaking: false 83 | description: added the fileone feature 84 | minor: true 85 | body: |- 86 | * This is a test repo 87 | * so ya! 88 | -------------------------------------------------------------------------------- /testdata/gold-init-changelog-with-merge-commit.yml: -------------------------------------------------------------------------------- 1 | - semver: 0.0.1 2 | date: 2024-01-21T15:37:29+01:00 3 | packager: Arne Jørgensen 4 | changes: 5 | - commit: e6f123d7d0aa2025ce61d28e2d34f94f3a6b5218 6 | note: Merge branch 'mergetest' 7 | author: 8 | name: Arne Jørgensen 9 | email: arne@arnested.dk 10 | committer: 11 | name: Arne Jørgensen 12 | email: arne@arnested.dk 13 | conventional_commit: 14 | category: chore 15 | scope: "" 16 | description: Merge branch 'mergetest' 17 | body: "" 18 | major: false 19 | minor: false 20 | patch: false 21 | - commit: 25c55c716f1f18bb91aafa55f98a02315f59afe6 22 | note: 'feat: add file to be merged in later' 23 | author: 24 | name: Arne Jørgensen 25 | email: arne@arnested.dk 26 | committer: 27 | name: Arne Jørgensen 28 | email: arne@arnested.dk 29 | conventional_commit: 30 | category: feat 31 | scope: "" 32 | description: add file to be merged in later 33 | body: "" 34 | major: false 35 | minor: true 36 | patch: false 37 | - commit: 2c499787328348f09ae1e8f03757c6483b9a938a 38 | note: |- 39 | oops i forgot to use Conventional Commits style message 40 | 41 | This should NOT break anything even if I am asking to build the changelog using Conventional Commits style message 42 | author: 43 | name: Dj Gilcrease 44 | email: d.gilcrease@f5.com 45 | committer: 46 | name: Dj Gilcrease 47 | email: d.gilcrease@f5.com 48 | conventional_commit: 49 | category: chore 50 | scope: "" 51 | description: oops i forgot to use Conventional Commits style message 52 | body: This should NOT break anything even if I am asking to build the changelog using Conventional Commits style message 53 | major: false 54 | minor: false 55 | patch: false 56 | - commit: 3ec1e9a60d07cc060cee727c97ffc8aac5713943 57 | note: |- 58 | feat: added file two feature 59 | 60 | BREAKING CHANGE: this is a backwards incompatible change 61 | author: 62 | name: Dj Gilcrease 63 | email: d.gilcrease@f5.com 64 | committer: 65 | name: Dj Gilcrease 66 | email: d.gilcrease@f5.com 67 | conventional_commit: 68 | category: feat 69 | scope: "" 70 | description: added file two feature 71 | body: "" 72 | footer: 73 | - 'BREAKING CHANGE: this is a backwards incompatible change' 74 | major: true 75 | minor: false 76 | patch: false 77 | - commit: 2cc00abc77d401a541d18c26e5c7fbef1effd3ed 78 | note: |- 79 | feat: added the fileone feature 80 | 81 | * This is a test repo 82 | * so ya! 83 | author: 84 | name: Dj Gilcrease 85 | email: d.gilcrease@f5.com 86 | committer: 87 | name: Dj Gilcrease 88 | email: d.gilcrease@f5.com 89 | conventional_commit: 90 | category: feat 91 | scope: "" 92 | description: added the fileone feature 93 | body: |- 94 | * This is a test repo 95 | * so ya! 96 | major: false 97 | minor: true 98 | patch: false 99 | -------------------------------------------------------------------------------- /testdata/gold-init-changelog-without-merge-commit.yml: -------------------------------------------------------------------------------- 1 | - semver: 0.0.1 2 | date: 2024-01-21T15:37:29+01:00 3 | packager: Arne Jørgensen 4 | changes: 5 | - commit: 25c55c716f1f18bb91aafa55f98a02315f59afe6 6 | note: 'feat: add file to be merged in later' 7 | author: 8 | name: Arne Jørgensen 9 | email: arne@arnested.dk 10 | committer: 11 | name: Arne Jørgensen 12 | email: arne@arnested.dk 13 | conventional_commit: 14 | category: feat 15 | scope: "" 16 | description: add file to be merged in later 17 | body: "" 18 | major: false 19 | minor: true 20 | patch: false 21 | - commit: 2c499787328348f09ae1e8f03757c6483b9a938a 22 | note: |- 23 | oops i forgot to use Conventional Commits style message 24 | 25 | This should NOT break anything even if I am asking to build the changelog using Conventional Commits style message 26 | author: 27 | name: Dj Gilcrease 28 | email: d.gilcrease@f5.com 29 | committer: 30 | name: Dj Gilcrease 31 | email: d.gilcrease@f5.com 32 | conventional_commit: 33 | category: chore 34 | scope: "" 35 | description: oops i forgot to use Conventional Commits style message 36 | body: This should NOT break anything even if I am asking to build the changelog using Conventional Commits style message 37 | major: false 38 | minor: false 39 | patch: false 40 | - commit: 3ec1e9a60d07cc060cee727c97ffc8aac5713943 41 | note: |- 42 | feat: added file two feature 43 | 44 | BREAKING CHANGE: this is a backwards incompatible change 45 | author: 46 | name: Dj Gilcrease 47 | email: d.gilcrease@f5.com 48 | committer: 49 | name: Dj Gilcrease 50 | email: d.gilcrease@f5.com 51 | conventional_commit: 52 | category: feat 53 | scope: "" 54 | description: added file two feature 55 | body: "" 56 | footer: 57 | - 'BREAKING CHANGE: this is a backwards incompatible change' 58 | major: true 59 | minor: false 60 | patch: false 61 | - commit: 2cc00abc77d401a541d18c26e5c7fbef1effd3ed 62 | note: |- 63 | feat: added the fileone feature 64 | 65 | * This is a test repo 66 | * so ya! 67 | author: 68 | name: Dj Gilcrease 69 | email: d.gilcrease@f5.com 70 | committer: 71 | name: Dj Gilcrease 72 | email: d.gilcrease@f5.com 73 | conventional_commit: 74 | category: feat 75 | scope: "" 76 | description: added the fileone feature 77 | body: |- 78 | * This is a test repo 79 | * so ya! 80 | major: false 81 | minor: true 82 | patch: false 83 | -------------------------------------------------------------------------------- /testdata/gold-init-changelog.yml: -------------------------------------------------------------------------------- 1 | - semver: 0.0.1 2 | date: 2019-10-18T16:05:33-07:00 3 | packager: Dj Gilcrease 4 | changes: 5 | - commit: 2c499787328348f09ae1e8f03757c6483b9a938a 6 | note: |- 7 | oops i forgot to use Conventional Commits style message 8 | 9 | This should NOT break anything even if I am asking to build the changelog using Conventional Commits style message 10 | author: 11 | name: Dj Gilcrease 12 | email: d.gilcrease@f5.com 13 | committer: 14 | name: Dj Gilcrease 15 | email: d.gilcrease@f5.com 16 | conventional_commit: 17 | category: "chore" 18 | scope: "" 19 | breaking: false 20 | description: oops i forgot to use Conventional Commits style message 21 | body: This should NOT break anything even if I am asking to build the changelog 22 | using Conventional Commits style message 23 | - commit: 3ec1e9a60d07cc060cee727c97ffc8aac5713943 24 | note: |- 25 | feat: added file two feature 26 | 27 | BREAKING CHANGE: this is a backwards incompatible change 28 | author: 29 | name: Dj Gilcrease 30 | email: d.gilcrease@f5.com 31 | committer: 32 | name: Dj Gilcrease 33 | email: d.gilcrease@f5.com 34 | conventional_commit: 35 | category: feat 36 | scope: "" 37 | breaking: true 38 | description: added file two feature 39 | major: true 40 | footer: 41 | - 'BREAKING CHANGE: this is a backwards incompatible change' 42 | - commit: 2cc00abc77d401a541d18c26e5c7fbef1effd3ed 43 | note: |- 44 | feat: added the fileone feature 45 | 46 | * This is a test repo 47 | * so ya! 48 | author: 49 | name: Dj Gilcrease 50 | email: d.gilcrease@f5.com 51 | committer: 52 | name: Dj Gilcrease 53 | email: d.gilcrease@f5.com 54 | conventional_commit: 55 | category: feat 56 | scope: "" 57 | breaking: false 58 | description: added the fileone feature 59 | minor: true 60 | body: |- 61 | * This is a test repo 62 | * so ya! 63 | -------------------------------------------------------------------------------- /testdata/gold-order-changelog.yml: -------------------------------------------------------------------------------- 1 | - semver: 0.10.0 2 | date: 2000-01-01T12:00:00+07:00 3 | packager: John Doe 4 | changes: 5 | - commit: 6cfa531ce3ac4bd4b4ec2522c2ab8d252e722a2c 6 | note: commit 10 7 | author: 8 | name: John Doe 9 | email: John.Doe@example.com 10 | committer: 11 | name: John Doe 12 | email: John.Doe@example.com 13 | - semver: 0.9.0 14 | date: 2000-01-01T12:00:00+07:00 15 | packager: John Doe 16 | changes: 17 | - commit: 66ec5a3714a3ea6dbaa8843f4d63cc377bf31d77 18 | note: commit 9 19 | author: 20 | name: John Doe 21 | email: John.Doe@example.com 22 | committer: 23 | name: John Doe 24 | email: John.Doe@example.com 25 | - semver: 0.8.0 26 | date: 2000-01-01T12:00:00+07:00 27 | packager: John Doe 28 | changes: 29 | - commit: 70b65ab708ffb8a0935da4bb8d3831f8c2230c5c 30 | note: commit 8 31 | author: 32 | name: John Doe 33 | email: John.Doe@example.com 34 | committer: 35 | name: John Doe 36 | email: John.Doe@example.com 37 | - semver: 0.7.0 38 | date: 2000-01-01T12:00:00+07:00 39 | packager: John Doe 40 | changes: 41 | - commit: c0510b7bd8342631a45a9ea67481ad0d9822f60b 42 | note: commit 7 43 | author: 44 | name: John Doe 45 | email: John.Doe@example.com 46 | committer: 47 | name: John Doe 48 | email: John.Doe@example.com 49 | - semver: 0.6.0 50 | date: 2000-01-01T12:00:00+07:00 51 | packager: John Doe 52 | changes: 53 | - commit: 677fb751e382174fc2d53068ee25f2d3863ccd19 54 | note: commit 6 55 | author: 56 | name: John Doe 57 | email: John.Doe@example.com 58 | committer: 59 | name: John Doe 60 | email: John.Doe@example.com 61 | - semver: 0.5.0 62 | date: 2000-01-01T12:00:00+07:00 63 | packager: John Doe 64 | changes: 65 | - commit: 85816484794feac8d53e0a13d14254972edbbddf 66 | note: commit 5 67 | author: 68 | name: John Doe 69 | email: John.Doe@example.com 70 | committer: 71 | name: John Doe 72 | email: John.Doe@example.com 73 | - semver: 0.4.0 74 | date: 2000-01-01T12:00:00+07:00 75 | packager: John Doe 76 | changes: 77 | - commit: 6ac70628dc692b5dec91438e133c97808256a035 78 | note: commit 4 79 | author: 80 | name: John Doe 81 | email: John.Doe@example.com 82 | committer: 83 | name: John Doe 84 | email: John.Doe@example.com 85 | - semver: 0.3.0 86 | date: 2000-01-01T12:00:00+07:00 87 | packager: John Doe 88 | changes: 89 | - commit: 6271fd4a63f296e3df46e4c0e7dcb948e2a3b066 90 | note: commit 3 91 | author: 92 | name: John Doe 93 | email: John.Doe@example.com 94 | committer: 95 | name: John Doe 96 | email: John.Doe@example.com 97 | - semver: 0.2.0 98 | date: 2000-01-01T12:00:00+07:00 99 | packager: John Doe 100 | changes: 101 | - commit: 6bfe64ae8f93108e5e0a85f43736459ac6ee0642 102 | note: commit 2 103 | author: 104 | name: John Doe 105 | email: John.Doe@example.com 106 | committer: 107 | name: John Doe 108 | email: John.Doe@example.com 109 | - semver: 0.1.0 110 | date: 2000-01-01T12:00:00+07:00 111 | packager: John Doe 112 | changes: 113 | - commit: 3b2811fc8b1a7a073ffb739e3766b47a3c81286d 114 | note: commit 1 115 | author: 116 | name: John Doe 117 | email: John.Doe@example.com 118 | committer: 119 | name: John Doe 120 | email: John.Doe@example.com 121 | - semver: 0.0.0 122 | date: 2000-01-01T12:00:00+07:00 123 | packager: John Doe 124 | changes: 125 | - commit: 1807a212fb257a8258a238d2028c6622fa71035c 126 | note: commit 0 127 | author: 128 | name: John Doe 129 | email: John.Doe@example.com 130 | committer: 131 | name: John Doe 132 | email: John.Doe@example.com 133 | -------------------------------------------------------------------------------- /testdata/init-repo-with-merge-commit/HEAD: -------------------------------------------------------------------------------- 1 | ref: refs/heads/master 2 | -------------------------------------------------------------------------------- /testdata/init-repo-with-merge-commit/config: -------------------------------------------------------------------------------- 1 | [core] 2 | repositoryformatversion = 0 3 | filemode = true 4 | bare = false 5 | -------------------------------------------------------------------------------- /testdata/init-repo-with-merge-commit/description: -------------------------------------------------------------------------------- 1 | Unnamed repository; edit this file 'description' to name the repository. 2 | -------------------------------------------------------------------------------- /testdata/init-repo-with-merge-commit/hooks/applypatch-msg.sample: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # 3 | # An example hook script to check the commit log message taken by 4 | # applypatch from an e-mail message. 5 | # 6 | # The hook should exit with non-zero status after issuing an 7 | # appropriate message if it wants to stop the commit. The hook is 8 | # allowed to edit the commit message file. 9 | # 10 | # To enable this hook, rename this file to "applypatch-msg". 11 | 12 | . git-sh-setup 13 | commitmsg="$(git rev-parse --git-path hooks/commit-msg)" 14 | test -x "$commitmsg" && exec "$commitmsg" ${1+"$@"} 15 | : 16 | -------------------------------------------------------------------------------- /testdata/init-repo-with-merge-commit/hooks/commit-msg.sample: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # 3 | # An example hook script to check the commit log message. 4 | # Called by "git commit" with one argument, the name of the file 5 | # that has the commit message. The hook should exit with non-zero 6 | # status after issuing an appropriate message if it wants to stop the 7 | # commit. The hook is allowed to edit the commit message file. 8 | # 9 | # To enable this hook, rename this file to "commit-msg". 10 | 11 | # Uncomment the below to add a Signed-off-by line to the message. 12 | # Doing this in a hook is a bad idea in general, but the prepare-commit-msg 13 | # hook is more suited to it. 14 | # 15 | # SOB=$(git var GIT_AUTHOR_IDENT | sed -n 's/^\(.*>\).*$/Signed-off-by: \1/p') 16 | # grep -qs "^$SOB" "$1" || echo "$SOB" >> "$1" 17 | 18 | # This example catches duplicate Signed-off-by lines. 19 | 20 | test "" = "$(grep '^Signed-off-by: ' "$1" | 21 | sort | uniq -c | sed -e '/^[ ]*1[ ]/d')" || { 22 | echo >&2 Duplicate Signed-off-by lines. 23 | exit 1 24 | } 25 | -------------------------------------------------------------------------------- /testdata/init-repo-with-merge-commit/hooks/fsmonitor-watchman.sample: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl 2 | 3 | use strict; 4 | use warnings; 5 | use IPC::Open2; 6 | 7 | # An example hook script to integrate Watchman 8 | # (https://facebook.github.io/watchman/) with git to speed up detecting 9 | # new and modified files. 10 | # 11 | # The hook is passed a version (currently 1) and a time in nanoseconds 12 | # formatted as a string and outputs to stdout all files that have been 13 | # modified since the given time. Paths must be relative to the root of 14 | # the working tree and separated by a single NUL. 15 | # 16 | # To enable this hook, rename this file to "query-watchman" and set 17 | # 'git config core.fsmonitor .git/hooks/query-watchman' 18 | # 19 | my ($version, $time) = @ARGV; 20 | 21 | # Check the hook interface version 22 | 23 | if ($version == 1) { 24 | # convert nanoseconds to seconds 25 | $time = int $time / 1000000000; 26 | } else { 27 | die "Unsupported query-fsmonitor hook version '$version'.\n" . 28 | "Falling back to scanning...\n"; 29 | } 30 | 31 | my $git_work_tree; 32 | if ($^O =~ 'msys' || $^O =~ 'cygwin') { 33 | $git_work_tree = Win32::GetCwd(); 34 | $git_work_tree =~ tr/\\/\//; 35 | } else { 36 | require Cwd; 37 | $git_work_tree = Cwd::cwd(); 38 | } 39 | 40 | my $retry = 1; 41 | 42 | launch_watchman(); 43 | 44 | sub launch_watchman { 45 | 46 | my $pid = open2(\*CHLD_OUT, \*CHLD_IN, 'watchman -j --no-pretty') 47 | or die "open2() failed: $!\n" . 48 | "Falling back to scanning...\n"; 49 | 50 | # In the query expression below we're asking for names of files that 51 | # changed since $time but were not transient (ie created after 52 | # $time but no longer exist). 53 | # 54 | # To accomplish this, we're using the "since" generator to use the 55 | # recency index to select candidate nodes and "fields" to limit the 56 | # output to file names only. Then we're using the "expression" term to 57 | # further constrain the results. 58 | # 59 | # The category of transient files that we want to ignore will have a 60 | # creation clock (cclock) newer than $time_t value and will also not 61 | # currently exist. 62 | 63 | my $query = <<" END"; 64 | ["query", "$git_work_tree", { 65 | "since": $time, 66 | "fields": ["name"], 67 | "expression": ["not", ["allof", ["since", $time, "cclock"], ["not", "exists"]]] 68 | }] 69 | END 70 | 71 | print CHLD_IN $query; 72 | close CHLD_IN; 73 | my $response = do {local $/; }; 74 | 75 | die "Watchman: command returned no output.\n" . 76 | "Falling back to scanning...\n" if $response eq ""; 77 | die "Watchman: command returned invalid output: $response\n" . 78 | "Falling back to scanning...\n" unless $response =~ /^\{/; 79 | 80 | my $json_pkg; 81 | eval { 82 | require JSON::XS; 83 | $json_pkg = "JSON::XS"; 84 | 1; 85 | } or do { 86 | require JSON::PP; 87 | $json_pkg = "JSON::PP"; 88 | }; 89 | 90 | my $o = $json_pkg->new->utf8->decode($response); 91 | 92 | if ($retry > 0 and $o->{error} and $o->{error} =~ m/unable to resolve root .* directory (.*) is not watched/) { 93 | print STDERR "Adding '$git_work_tree' to watchman's watch list.\n"; 94 | $retry--; 95 | qx/watchman watch "$git_work_tree"/; 96 | die "Failed to make watchman watch '$git_work_tree'.\n" . 97 | "Falling back to scanning...\n" if $? != 0; 98 | 99 | # Watchman will always return all files on the first query so 100 | # return the fast "everything is dirty" flag to git and do the 101 | # Watchman query just to get it over with now so we won't pay 102 | # the cost in git to look up each individual file. 103 | print "/\0"; 104 | eval { launch_watchman() }; 105 | exit 0; 106 | } 107 | 108 | die "Watchman: $o->{error}.\n" . 109 | "Falling back to scanning...\n" if $o->{error}; 110 | 111 | binmode STDOUT, ":utf8"; 112 | local $, = "\0"; 113 | print @{$o->{files}}; 114 | } 115 | -------------------------------------------------------------------------------- /testdata/init-repo-with-merge-commit/hooks/post-update.sample: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # 3 | # An example hook script to prepare a packed repository for use over 4 | # dumb transports. 5 | # 6 | # To enable this hook, rename this file to "post-update". 7 | 8 | exec git update-server-info 9 | -------------------------------------------------------------------------------- /testdata/init-repo-with-merge-commit/hooks/pre-applypatch.sample: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # 3 | # An example hook script to verify what is about to be committed 4 | # by applypatch from an e-mail message. 5 | # 6 | # The hook should exit with non-zero status after issuing an 7 | # appropriate message if it wants to stop the commit. 8 | # 9 | # To enable this hook, rename this file to "pre-applypatch". 10 | 11 | . git-sh-setup 12 | precommit="$(git rev-parse --git-path hooks/pre-commit)" 13 | test -x "$precommit" && exec "$precommit" ${1+"$@"} 14 | : 15 | -------------------------------------------------------------------------------- /testdata/init-repo-with-merge-commit/hooks/pre-commit.sample: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # 3 | # An example hook script to verify what is about to be committed. 4 | # Called by "git commit" with no arguments. The hook should 5 | # exit with non-zero status after issuing an appropriate message if 6 | # it wants to stop the commit. 7 | # 8 | # To enable this hook, rename this file to "pre-commit". 9 | 10 | if git rev-parse --verify HEAD >/dev/null 2>&1 11 | then 12 | against=HEAD 13 | else 14 | # Initial commit: diff against an empty tree object 15 | against=$(git hash-object -t tree /dev/null) 16 | fi 17 | 18 | # If you want to allow non-ASCII filenames set this variable to true. 19 | allownonascii=$(git config --bool hooks.allownonascii) 20 | 21 | # Redirect output to stderr. 22 | exec 1>&2 23 | 24 | # Cross platform projects tend to avoid non-ASCII filenames; prevent 25 | # them from being added to the repository. We exploit the fact that the 26 | # printable range starts at the space character and ends with tilde. 27 | if [ "$allownonascii" != "true" ] && 28 | # Note that the use of brackets around a tr range is ok here, (it's 29 | # even required, for portability to Solaris 10's /usr/bin/tr), since 30 | # the square bracket bytes happen to fall in the designated range. 31 | test $(git diff --cached --name-only --diff-filter=A -z $against | 32 | LC_ALL=C tr -d '[ -~]\0' | wc -c) != 0 33 | then 34 | cat <<\EOF 35 | Error: Attempt to add a non-ASCII file name. 36 | 37 | This can cause problems if you want to work with people on other platforms. 38 | 39 | To be portable it is advisable to rename the file. 40 | 41 | If you know what you are doing you can disable this check using: 42 | 43 | git config hooks.allownonascii true 44 | EOF 45 | exit 1 46 | fi 47 | 48 | # If there are whitespace errors, print the offending file names and fail. 49 | exec git diff-index --check --cached $against -- 50 | -------------------------------------------------------------------------------- /testdata/init-repo-with-merge-commit/hooks/pre-push.sample: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # An example hook script to verify what is about to be pushed. Called by "git 4 | # push" after it has checked the remote status, but before anything has been 5 | # pushed. If this script exits with a non-zero status nothing will be pushed. 6 | # 7 | # This hook is called with the following parameters: 8 | # 9 | # $1 -- Name of the remote to which the push is being done 10 | # $2 -- URL to which the push is being done 11 | # 12 | # If pushing without using a named remote those arguments will be equal. 13 | # 14 | # Information about the commits which are being pushed is supplied as lines to 15 | # the standard input in the form: 16 | # 17 | # 18 | # 19 | # This sample shows how to prevent push of commits where the log message starts 20 | # with "WIP" (work in progress). 21 | 22 | remote="$1" 23 | url="$2" 24 | 25 | z40=0000000000000000000000000000000000000000 26 | 27 | while read local_ref local_sha remote_ref remote_sha 28 | do 29 | if [ "$local_sha" = $z40 ] 30 | then 31 | # Handle delete 32 | : 33 | else 34 | if [ "$remote_sha" = $z40 ] 35 | then 36 | # New branch, examine all commits 37 | range="$local_sha" 38 | else 39 | # Update to existing branch, examine new commits 40 | range="$remote_sha..$local_sha" 41 | fi 42 | 43 | # Check for WIP commit 44 | commit=`git rev-list -n 1 --grep '^WIP' "$range"` 45 | if [ -n "$commit" ] 46 | then 47 | echo >&2 "Found WIP commit in $local_ref, not pushing" 48 | exit 1 49 | fi 50 | fi 51 | done 52 | 53 | exit 0 54 | -------------------------------------------------------------------------------- /testdata/init-repo-with-merge-commit/hooks/pre-rebase.sample: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # 3 | # Copyright (c) 2006, 2008 Junio C Hamano 4 | # 5 | # The "pre-rebase" hook is run just before "git rebase" starts doing 6 | # its job, and can prevent the command from running by exiting with 7 | # non-zero status. 8 | # 9 | # The hook is called with the following parameters: 10 | # 11 | # $1 -- the upstream the series was forked from. 12 | # $2 -- the branch being rebased (or empty when rebasing the current branch). 13 | # 14 | # This sample shows how to prevent topic branches that are already 15 | # merged to 'next' branch from getting rebased, because allowing it 16 | # would result in rebasing already published history. 17 | 18 | publish=next 19 | basebranch="$1" 20 | if test "$#" = 2 21 | then 22 | topic="refs/heads/$2" 23 | else 24 | topic=`git symbolic-ref HEAD` || 25 | exit 0 ;# we do not interrupt rebasing detached HEAD 26 | fi 27 | 28 | case "$topic" in 29 | refs/heads/??/*) 30 | ;; 31 | *) 32 | exit 0 ;# we do not interrupt others. 33 | ;; 34 | esac 35 | 36 | # Now we are dealing with a topic branch being rebased 37 | # on top of master. Is it OK to rebase it? 38 | 39 | # Does the topic really exist? 40 | git show-ref -q "$topic" || { 41 | echo >&2 "No such branch $topic" 42 | exit 1 43 | } 44 | 45 | # Is topic fully merged to master? 46 | not_in_master=`git rev-list --pretty=oneline ^master "$topic"` 47 | if test -z "$not_in_master" 48 | then 49 | echo >&2 "$topic is fully merged to master; better remove it." 50 | exit 1 ;# we could allow it, but there is no point. 51 | fi 52 | 53 | # Is topic ever merged to next? If so you should not be rebasing it. 54 | only_next_1=`git rev-list ^master "^$topic" ${publish} | sort` 55 | only_next_2=`git rev-list ^master ${publish} | sort` 56 | if test "$only_next_1" = "$only_next_2" 57 | then 58 | not_in_topic=`git rev-list "^$topic" master` 59 | if test -z "$not_in_topic" 60 | then 61 | echo >&2 "$topic is already up to date with master" 62 | exit 1 ;# we could allow it, but there is no point. 63 | else 64 | exit 0 65 | fi 66 | else 67 | not_in_next=`git rev-list --pretty=oneline ^${publish} "$topic"` 68 | /usr/bin/perl -e ' 69 | my $topic = $ARGV[0]; 70 | my $msg = "* $topic has commits already merged to public branch:\n"; 71 | my (%not_in_next) = map { 72 | /^([0-9a-f]+) /; 73 | ($1 => 1); 74 | } split(/\n/, $ARGV[1]); 75 | for my $elem (map { 76 | /^([0-9a-f]+) (.*)$/; 77 | [$1 => $2]; 78 | } split(/\n/, $ARGV[2])) { 79 | if (!exists $not_in_next{$elem->[0]}) { 80 | if ($msg) { 81 | print STDERR $msg; 82 | undef $msg; 83 | } 84 | print STDERR " $elem->[1]\n"; 85 | } 86 | } 87 | ' "$topic" "$not_in_next" "$not_in_master" 88 | exit 1 89 | fi 90 | 91 | <<\DOC_END 92 | 93 | This sample hook safeguards topic branches that have been 94 | published from being rewound. 95 | 96 | The workflow assumed here is: 97 | 98 | * Once a topic branch forks from "master", "master" is never 99 | merged into it again (either directly or indirectly). 100 | 101 | * Once a topic branch is fully cooked and merged into "master", 102 | it is deleted. If you need to build on top of it to correct 103 | earlier mistakes, a new topic branch is created by forking at 104 | the tip of the "master". This is not strictly necessary, but 105 | it makes it easier to keep your history simple. 106 | 107 | * Whenever you need to test or publish your changes to topic 108 | branches, merge them into "next" branch. 109 | 110 | The script, being an example, hardcodes the publish branch name 111 | to be "next", but it is trivial to make it configurable via 112 | $GIT_DIR/config mechanism. 113 | 114 | With this workflow, you would want to know: 115 | 116 | (1) ... if a topic branch has ever been merged to "next". Young 117 | topic branches can have stupid mistakes you would rather 118 | clean up before publishing, and things that have not been 119 | merged into other branches can be easily rebased without 120 | affecting other people. But once it is published, you would 121 | not want to rewind it. 122 | 123 | (2) ... if a topic branch has been fully merged to "master". 124 | Then you can delete it. More importantly, you should not 125 | build on top of it -- other people may already want to 126 | change things related to the topic as patches against your 127 | "master", so if you need further changes, it is better to 128 | fork the topic (perhaps with the same name) afresh from the 129 | tip of "master". 130 | 131 | Let's look at this example: 132 | 133 | o---o---o---o---o---o---o---o---o---o "next" 134 | / / / / 135 | / a---a---b A / / 136 | / / / / 137 | / / c---c---c---c B / 138 | / / / \ / 139 | / / / b---b C \ / 140 | / / / / \ / 141 | ---o---o---o---o---o---o---o---o---o---o---o "master" 142 | 143 | 144 | A, B and C are topic branches. 145 | 146 | * A has one fix since it was merged up to "next". 147 | 148 | * B has finished. It has been fully merged up to "master" and "next", 149 | and is ready to be deleted. 150 | 151 | * C has not merged to "next" at all. 152 | 153 | We would want to allow C to be rebased, refuse A, and encourage 154 | B to be deleted. 155 | 156 | To compute (1): 157 | 158 | git rev-list ^master ^topic next 159 | git rev-list ^master next 160 | 161 | if these match, topic has not merged in next at all. 162 | 163 | To compute (2): 164 | 165 | git rev-list master..topic 166 | 167 | if this is empty, it is fully merged to "master". 168 | 169 | DOC_END 170 | -------------------------------------------------------------------------------- /testdata/init-repo-with-merge-commit/hooks/pre-receive.sample: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # 3 | # An example hook script to make use of push options. 4 | # The example simply echoes all push options that start with 'echoback=' 5 | # and rejects all pushes when the "reject" push option is used. 6 | # 7 | # To enable this hook, rename this file to "pre-receive". 8 | 9 | if test -n "$GIT_PUSH_OPTION_COUNT" 10 | then 11 | i=0 12 | while test "$i" -lt "$GIT_PUSH_OPTION_COUNT" 13 | do 14 | eval "value=\$GIT_PUSH_OPTION_$i" 15 | case "$value" in 16 | echoback=*) 17 | echo "echo from the pre-receive-hook: ${value#*=}" >&2 18 | ;; 19 | reject) 20 | exit 1 21 | esac 22 | i=$((i + 1)) 23 | done 24 | fi 25 | -------------------------------------------------------------------------------- /testdata/init-repo-with-merge-commit/hooks/prepare-commit-msg.sample: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # 3 | # An example hook script to prepare the commit log message. 4 | # Called by "git commit" with the name of the file that has the 5 | # commit message, followed by the description of the commit 6 | # message's source. The hook's purpose is to edit the commit 7 | # message file. If the hook fails with a non-zero status, 8 | # the commit is aborted. 9 | # 10 | # To enable this hook, rename this file to "prepare-commit-msg". 11 | 12 | # This hook includes three examples. The first one removes the 13 | # "# Please enter the commit message..." help message. 14 | # 15 | # The second includes the output of "git diff --name-status -r" 16 | # into the message, just before the "git status" output. It is 17 | # commented because it doesn't cope with --amend or with squashed 18 | # commits. 19 | # 20 | # The third example adds a Signed-off-by line to the message, that can 21 | # still be edited. This is rarely a good idea. 22 | 23 | COMMIT_MSG_FILE=$1 24 | COMMIT_SOURCE=$2 25 | SHA1=$3 26 | 27 | /usr/bin/perl -i.bak -ne 'print unless(m/^. Please enter the commit message/..m/^#$/)' "$COMMIT_MSG_FILE" 28 | 29 | # case "$COMMIT_SOURCE,$SHA1" in 30 | # ,|template,) 31 | # /usr/bin/perl -i.bak -pe ' 32 | # print "\n" . `git diff --cached --name-status -r` 33 | # if /^#/ && $first++ == 0' "$COMMIT_MSG_FILE" ;; 34 | # *) ;; 35 | # esac 36 | 37 | # SOB=$(git var GIT_COMMITTER_IDENT | sed -n 's/^\(.*>\).*$/Signed-off-by: \1/p') 38 | # git interpret-trailers --in-place --trailer "$SOB" "$COMMIT_MSG_FILE" 39 | # if test -z "$COMMIT_SOURCE" 40 | # then 41 | # /usr/bin/perl -i.bak -pe 'print "\n" if !$first_line++' "$COMMIT_MSG_FILE" 42 | # fi 43 | -------------------------------------------------------------------------------- /testdata/init-repo-with-merge-commit/hooks/update.sample: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # 3 | # An example hook script to block unannotated tags from entering. 4 | # Called by "git receive-pack" with arguments: refname sha1-old sha1-new 5 | # 6 | # To enable this hook, rename this file to "update". 7 | # 8 | # Config 9 | # ------ 10 | # hooks.allowunannotated 11 | # This boolean sets whether unannotated tags will be allowed into the 12 | # repository. By default they won't be. 13 | # hooks.allowdeletetag 14 | # This boolean sets whether deleting tags will be allowed in the 15 | # repository. By default they won't be. 16 | # hooks.allowmodifytag 17 | # This boolean sets whether a tag may be modified after creation. By default 18 | # it won't be. 19 | # hooks.allowdeletebranch 20 | # This boolean sets whether deleting branches will be allowed in the 21 | # repository. By default they won't be. 22 | # hooks.denycreatebranch 23 | # This boolean sets whether remotely creating branches will be denied 24 | # in the repository. By default this is allowed. 25 | # 26 | 27 | # --- Command line 28 | refname="$1" 29 | oldrev="$2" 30 | newrev="$3" 31 | 32 | # --- Safety check 33 | if [ -z "$GIT_DIR" ]; then 34 | echo "Don't run this script from the command line." >&2 35 | echo " (if you want, you could supply GIT_DIR then run" >&2 36 | echo " $0 )" >&2 37 | exit 1 38 | fi 39 | 40 | if [ -z "$refname" -o -z "$oldrev" -o -z "$newrev" ]; then 41 | echo "usage: $0 " >&2 42 | exit 1 43 | fi 44 | 45 | # --- Config 46 | allowunannotated=$(git config --bool hooks.allowunannotated) 47 | allowdeletebranch=$(git config --bool hooks.allowdeletebranch) 48 | denycreatebranch=$(git config --bool hooks.denycreatebranch) 49 | allowdeletetag=$(git config --bool hooks.allowdeletetag) 50 | allowmodifytag=$(git config --bool hooks.allowmodifytag) 51 | 52 | # check for no description 53 | projectdesc=$(sed -e '1q' "$GIT_DIR/description") 54 | case "$projectdesc" in 55 | "Unnamed repository"* | "") 56 | echo "*** Project description file hasn't been set" >&2 57 | exit 1 58 | ;; 59 | esac 60 | 61 | # --- Check types 62 | # if $newrev is 0000...0000, it's a commit to delete a ref. 63 | zero="0000000000000000000000000000000000000000" 64 | if [ "$newrev" = "$zero" ]; then 65 | newrev_type=delete 66 | else 67 | newrev_type=$(git cat-file -t $newrev) 68 | fi 69 | 70 | case "$refname","$newrev_type" in 71 | refs/tags/*,commit) 72 | # un-annotated tag 73 | short_refname=${refname##refs/tags/} 74 | if [ "$allowunannotated" != "true" ]; then 75 | echo "*** The un-annotated tag, $short_refname, is not allowed in this repository" >&2 76 | echo "*** Use 'git tag [ -a | -s ]' for tags you want to propagate." >&2 77 | exit 1 78 | fi 79 | ;; 80 | refs/tags/*,delete) 81 | # delete tag 82 | if [ "$allowdeletetag" != "true" ]; then 83 | echo "*** Deleting a tag is not allowed in this repository" >&2 84 | exit 1 85 | fi 86 | ;; 87 | refs/tags/*,tag) 88 | # annotated tag 89 | if [ "$allowmodifytag" != "true" ] && git rev-parse $refname > /dev/null 2>&1 90 | then 91 | echo "*** Tag '$refname' already exists." >&2 92 | echo "*** Modifying a tag is not allowed in this repository." >&2 93 | exit 1 94 | fi 95 | ;; 96 | refs/heads/*,commit) 97 | # branch 98 | if [ "$oldrev" = "$zero" -a "$denycreatebranch" = "true" ]; then 99 | echo "*** Creating a branch is not allowed in this repository" >&2 100 | exit 1 101 | fi 102 | ;; 103 | refs/heads/*,delete) 104 | # delete branch 105 | if [ "$allowdeletebranch" != "true" ]; then 106 | echo "*** Deleting a branch is not allowed in this repository" >&2 107 | exit 1 108 | fi 109 | ;; 110 | refs/remotes/*,commit) 111 | # tracking branch 112 | ;; 113 | refs/remotes/*,delete) 114 | # delete tracking branch 115 | if [ "$allowdeletebranch" != "true" ]; then 116 | echo "*** Deleting a tracking branch is not allowed in this repository" >&2 117 | exit 1 118 | fi 119 | ;; 120 | *) 121 | # Anything else (is there anything else?) 122 | echo "*** Update hook: unknown type of update to ref $refname of type $newrev_type" >&2 123 | exit 1 124 | ;; 125 | esac 126 | 127 | # --- Finished 128 | exit 0 129 | -------------------------------------------------------------------------------- /testdata/init-repo-with-merge-commit/info/exclude: -------------------------------------------------------------------------------- 1 | # git ls-files --others --exclude-from=.git/info/exclude 2 | # Lines that start with '#' are comments. 3 | # For a project mostly in C, the following would be a good set of 4 | # exclude patterns (uncomment them if you want to use them): 5 | # *.[oa] 6 | # *~ 7 | -------------------------------------------------------------------------------- /testdata/init-repo-with-merge-commit/objects/25/c55c716f1f18bb91aafa55f98a02315f59afe6: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/goreleaser/chglog/b46d97bd57b12f8c0dc317dcd0568dff5db5b396/testdata/init-repo-with-merge-commit/objects/25/c55c716f1f18bb91aafa55f98a02315f59afe6 -------------------------------------------------------------------------------- /testdata/init-repo-with-merge-commit/objects/2c/499787328348f09ae1e8f03757c6483b9a938a: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/goreleaser/chglog/b46d97bd57b12f8c0dc317dcd0568dff5db5b396/testdata/init-repo-with-merge-commit/objects/2c/499787328348f09ae1e8f03757c6483b9a938a -------------------------------------------------------------------------------- /testdata/init-repo-with-merge-commit/objects/2c/c00abc77d401a541d18c26e5c7fbef1effd3ed: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/goreleaser/chglog/b46d97bd57b12f8c0dc317dcd0568dff5db5b396/testdata/init-repo-with-merge-commit/objects/2c/c00abc77d401a541d18c26e5c7fbef1effd3ed -------------------------------------------------------------------------------- /testdata/init-repo-with-merge-commit/objects/3e/c1e9a60d07cc060cee727c97ffc8aac5713943: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/goreleaser/chglog/b46d97bd57b12f8c0dc317dcd0568dff5db5b396/testdata/init-repo-with-merge-commit/objects/3e/c1e9a60d07cc060cee727c97ffc8aac5713943 -------------------------------------------------------------------------------- /testdata/init-repo-with-merge-commit/objects/6f/59e5e770be445518775b22bbe25e89339b778b: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/goreleaser/chglog/b46d97bd57b12f8c0dc317dcd0568dff5db5b396/testdata/init-repo-with-merge-commit/objects/6f/59e5e770be445518775b22bbe25e89339b778b -------------------------------------------------------------------------------- /testdata/init-repo-with-merge-commit/objects/ab/515204e62e6fd2b468736de7aadcaca4d9dffd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/goreleaser/chglog/b46d97bd57b12f8c0dc317dcd0568dff5db5b396/testdata/init-repo-with-merge-commit/objects/ab/515204e62e6fd2b468736de7aadcaca4d9dffd -------------------------------------------------------------------------------- /testdata/init-repo-with-merge-commit/objects/d3/0b6cdff6d118d97095f39b2982d4fa237d33d1: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/goreleaser/chglog/b46d97bd57b12f8c0dc317dcd0568dff5db5b396/testdata/init-repo-with-merge-commit/objects/d3/0b6cdff6d118d97095f39b2982d4fa237d33d1 -------------------------------------------------------------------------------- /testdata/init-repo-with-merge-commit/objects/e6/9de29bb2d1d6434b8b29ae775ad8c2e48c5391: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/goreleaser/chglog/b46d97bd57b12f8c0dc317dcd0568dff5db5b396/testdata/init-repo-with-merge-commit/objects/e6/9de29bb2d1d6434b8b29ae775ad8c2e48c5391 -------------------------------------------------------------------------------- /testdata/init-repo-with-merge-commit/objects/e6/f123d7d0aa2025ce61d28e2d34f94f3a6b5218: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/goreleaser/chglog/b46d97bd57b12f8c0dc317dcd0568dff5db5b396/testdata/init-repo-with-merge-commit/objects/e6/f123d7d0aa2025ce61d28e2d34f94f3a6b5218 -------------------------------------------------------------------------------- /testdata/init-repo-with-merge-commit/objects/ea/2b12e15f99419047f49d5fc5dc844073899da8: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/goreleaser/chglog/b46d97bd57b12f8c0dc317dcd0568dff5db5b396/testdata/init-repo-with-merge-commit/objects/ea/2b12e15f99419047f49d5fc5dc844073899da8 -------------------------------------------------------------------------------- /testdata/init-repo-with-merge-commit/packed-refs: -------------------------------------------------------------------------------- 1 | # pack-refs with: peeled fully-peeled sorted 2 | -------------------------------------------------------------------------------- /testdata/init-repo-with-merge-commit/refs/heads/master: -------------------------------------------------------------------------------- 1 | e6f123d7d0aa2025ce61d28e2d34f94f3a6b5218 2 | -------------------------------------------------------------------------------- /testdata/init-repo-with-merge-commit/refs/tags/v0.0.1: -------------------------------------------------------------------------------- 1 | e6f123d7d0aa2025ce61d28e2d34f94f3a6b5218 2 | -------------------------------------------------------------------------------- /testdata/init-repo/HEAD: -------------------------------------------------------------------------------- 1 | ref: refs/heads/master 2 | -------------------------------------------------------------------------------- /testdata/init-repo/config: -------------------------------------------------------------------------------- 1 | [core] 2 | repositoryformatversion = 0 3 | filemode = true 4 | bare = true 5 | -------------------------------------------------------------------------------- /testdata/init-repo/description: -------------------------------------------------------------------------------- 1 | Unnamed repository; edit this file 'description' to name the repository. 2 | -------------------------------------------------------------------------------- /testdata/init-repo/hooks/applypatch-msg.sample: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # 3 | # An example hook script to check the commit log message taken by 4 | # applypatch from an e-mail message. 5 | # 6 | # The hook should exit with non-zero status after issuing an 7 | # appropriate message if it wants to stop the commit. The hook is 8 | # allowed to edit the commit message file. 9 | # 10 | # To enable this hook, rename this file to "applypatch-msg". 11 | 12 | . git-sh-setup 13 | commitmsg="$(git rev-parse --git-path hooks/commit-msg)" 14 | test -x "$commitmsg" && exec "$commitmsg" ${1+"$@"} 15 | : 16 | -------------------------------------------------------------------------------- /testdata/init-repo/hooks/commit-msg.sample: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # 3 | # An example hook script to check the commit log message. 4 | # Called by "git commit" with one argument, the name of the file 5 | # that has the commit message. The hook should exit with non-zero 6 | # status after issuing an appropriate message if it wants to stop the 7 | # commit. The hook is allowed to edit the commit message file. 8 | # 9 | # To enable this hook, rename this file to "commit-msg". 10 | 11 | # Uncomment the below to add a Signed-off-by line to the message. 12 | # Doing this in a hook is a bad idea in general, but the prepare-commit-msg 13 | # hook is more suited to it. 14 | # 15 | # SOB=$(git var GIT_AUTHOR_IDENT | sed -n 's/^\(.*>\).*$/Signed-off-by: \1/p') 16 | # grep -qs "^$SOB" "$1" || echo "$SOB" >> "$1" 17 | 18 | # This example catches duplicate Signed-off-by lines. 19 | 20 | test "" = "$(grep '^Signed-off-by: ' "$1" | 21 | sort | uniq -c | sed -e '/^[ ]*1[ ]/d')" || { 22 | echo >&2 Duplicate Signed-off-by lines. 23 | exit 1 24 | } 25 | -------------------------------------------------------------------------------- /testdata/init-repo/hooks/fsmonitor-watchman.sample: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl 2 | 3 | use strict; 4 | use warnings; 5 | use IPC::Open2; 6 | 7 | # An example hook script to integrate Watchman 8 | # (https://facebook.github.io/watchman/) with git to speed up detecting 9 | # new and modified files. 10 | # 11 | # The hook is passed a version (currently 1) and a time in nanoseconds 12 | # formatted as a string and outputs to stdout all files that have been 13 | # modified since the given time. Paths must be relative to the root of 14 | # the working tree and separated by a single NUL. 15 | # 16 | # To enable this hook, rename this file to "query-watchman" and set 17 | # 'git config core.fsmonitor .git/hooks/query-watchman' 18 | # 19 | my ($version, $time) = @ARGV; 20 | 21 | # Check the hook interface version 22 | 23 | if ($version == 1) { 24 | # convert nanoseconds to seconds 25 | $time = int $time / 1000000000; 26 | } else { 27 | die "Unsupported query-fsmonitor hook version '$version'.\n" . 28 | "Falling back to scanning...\n"; 29 | } 30 | 31 | my $git_work_tree; 32 | if ($^O =~ 'msys' || $^O =~ 'cygwin') { 33 | $git_work_tree = Win32::GetCwd(); 34 | $git_work_tree =~ tr/\\/\//; 35 | } else { 36 | require Cwd; 37 | $git_work_tree = Cwd::cwd(); 38 | } 39 | 40 | my $retry = 1; 41 | 42 | launch_watchman(); 43 | 44 | sub launch_watchman { 45 | 46 | my $pid = open2(\*CHLD_OUT, \*CHLD_IN, 'watchman -j --no-pretty') 47 | or die "open2() failed: $!\n" . 48 | "Falling back to scanning...\n"; 49 | 50 | # In the query expression below we're asking for names of files that 51 | # changed since $time but were not transient (ie created after 52 | # $time but no longer exist). 53 | # 54 | # To accomplish this, we're using the "since" generator to use the 55 | # recency index to select candidate nodes and "fields" to limit the 56 | # output to file names only. Then we're using the "expression" term to 57 | # further constrain the results. 58 | # 59 | # The category of transient files that we want to ignore will have a 60 | # creation clock (cclock) newer than $time_t value and will also not 61 | # currently exist. 62 | 63 | my $query = <<" END"; 64 | ["query", "$git_work_tree", { 65 | "since": $time, 66 | "fields": ["name"], 67 | "expression": ["not", ["allof", ["since", $time, "cclock"], ["not", "exists"]]] 68 | }] 69 | END 70 | 71 | print CHLD_IN $query; 72 | close CHLD_IN; 73 | my $response = do {local $/; }; 74 | 75 | die "Watchman: command returned no output.\n" . 76 | "Falling back to scanning...\n" if $response eq ""; 77 | die "Watchman: command returned invalid output: $response\n" . 78 | "Falling back to scanning...\n" unless $response =~ /^\{/; 79 | 80 | my $json_pkg; 81 | eval { 82 | require JSON::XS; 83 | $json_pkg = "JSON::XS"; 84 | 1; 85 | } or do { 86 | require JSON::PP; 87 | $json_pkg = "JSON::PP"; 88 | }; 89 | 90 | my $o = $json_pkg->new->utf8->decode($response); 91 | 92 | if ($retry > 0 and $o->{error} and $o->{error} =~ m/unable to resolve root .* directory (.*) is not watched/) { 93 | print STDERR "Adding '$git_work_tree' to watchman's watch list.\n"; 94 | $retry--; 95 | qx/watchman watch "$git_work_tree"/; 96 | die "Failed to make watchman watch '$git_work_tree'.\n" . 97 | "Falling back to scanning...\n" if $? != 0; 98 | 99 | # Watchman will always return all files on the first query so 100 | # return the fast "everything is dirty" flag to git and do the 101 | # Watchman query just to get it over with now so we won't pay 102 | # the cost in git to look up each individual file. 103 | print "/\0"; 104 | eval { launch_watchman() }; 105 | exit 0; 106 | } 107 | 108 | die "Watchman: $o->{error}.\n" . 109 | "Falling back to scanning...\n" if $o->{error}; 110 | 111 | binmode STDOUT, ":utf8"; 112 | local $, = "\0"; 113 | print @{$o->{files}}; 114 | } 115 | -------------------------------------------------------------------------------- /testdata/init-repo/hooks/post-update.sample: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # 3 | # An example hook script to prepare a packed repository for use over 4 | # dumb transports. 5 | # 6 | # To enable this hook, rename this file to "post-update". 7 | 8 | exec git update-server-info 9 | -------------------------------------------------------------------------------- /testdata/init-repo/hooks/pre-applypatch.sample: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # 3 | # An example hook script to verify what is about to be committed 4 | # by applypatch from an e-mail message. 5 | # 6 | # The hook should exit with non-zero status after issuing an 7 | # appropriate message if it wants to stop the commit. 8 | # 9 | # To enable this hook, rename this file to "pre-applypatch". 10 | 11 | . git-sh-setup 12 | precommit="$(git rev-parse --git-path hooks/pre-commit)" 13 | test -x "$precommit" && exec "$precommit" ${1+"$@"} 14 | : 15 | -------------------------------------------------------------------------------- /testdata/init-repo/hooks/pre-commit.sample: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # 3 | # An example hook script to verify what is about to be committed. 4 | # Called by "git commit" with no arguments. The hook should 5 | # exit with non-zero status after issuing an appropriate message if 6 | # it wants to stop the commit. 7 | # 8 | # To enable this hook, rename this file to "pre-commit". 9 | 10 | if git rev-parse --verify HEAD >/dev/null 2>&1 11 | then 12 | against=HEAD 13 | else 14 | # Initial commit: diff against an empty tree object 15 | against=$(git hash-object -t tree /dev/null) 16 | fi 17 | 18 | # If you want to allow non-ASCII filenames set this variable to true. 19 | allownonascii=$(git config --bool hooks.allownonascii) 20 | 21 | # Redirect output to stderr. 22 | exec 1>&2 23 | 24 | # Cross platform projects tend to avoid non-ASCII filenames; prevent 25 | # them from being added to the repository. We exploit the fact that the 26 | # printable range starts at the space character and ends with tilde. 27 | if [ "$allownonascii" != "true" ] && 28 | # Note that the use of brackets around a tr range is ok here, (it's 29 | # even required, for portability to Solaris 10's /usr/bin/tr), since 30 | # the square bracket bytes happen to fall in the designated range. 31 | test $(git diff --cached --name-only --diff-filter=A -z $against | 32 | LC_ALL=C tr -d '[ -~]\0' | wc -c) != 0 33 | then 34 | cat <<\EOF 35 | Error: Attempt to add a non-ASCII file name. 36 | 37 | This can cause problems if you want to work with people on other platforms. 38 | 39 | To be portable it is advisable to rename the file. 40 | 41 | If you know what you are doing you can disable this check using: 42 | 43 | git config hooks.allownonascii true 44 | EOF 45 | exit 1 46 | fi 47 | 48 | # If there are whitespace errors, print the offending file names and fail. 49 | exec git diff-index --check --cached $against -- 50 | -------------------------------------------------------------------------------- /testdata/init-repo/hooks/pre-push.sample: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # An example hook script to verify what is about to be pushed. Called by "git 4 | # push" after it has checked the remote status, but before anything has been 5 | # pushed. If this script exits with a non-zero status nothing will be pushed. 6 | # 7 | # This hook is called with the following parameters: 8 | # 9 | # $1 -- Name of the remote to which the push is being done 10 | # $2 -- URL to which the push is being done 11 | # 12 | # If pushing without using a named remote those arguments will be equal. 13 | # 14 | # Information about the commits which are being pushed is supplied as lines to 15 | # the standard input in the form: 16 | # 17 | # 18 | # 19 | # This sample shows how to prevent push of commits where the log message starts 20 | # with "WIP" (work in progress). 21 | 22 | remote="$1" 23 | url="$2" 24 | 25 | z40=0000000000000000000000000000000000000000 26 | 27 | while read local_ref local_sha remote_ref remote_sha 28 | do 29 | if [ "$local_sha" = $z40 ] 30 | then 31 | # Handle delete 32 | : 33 | else 34 | if [ "$remote_sha" = $z40 ] 35 | then 36 | # New branch, examine all commits 37 | range="$local_sha" 38 | else 39 | # Update to existing branch, examine new commits 40 | range="$remote_sha..$local_sha" 41 | fi 42 | 43 | # Check for WIP commit 44 | commit=`git rev-list -n 1 --grep '^WIP' "$range"` 45 | if [ -n "$commit" ] 46 | then 47 | echo >&2 "Found WIP commit in $local_ref, not pushing" 48 | exit 1 49 | fi 50 | fi 51 | done 52 | 53 | exit 0 54 | -------------------------------------------------------------------------------- /testdata/init-repo/hooks/pre-rebase.sample: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # 3 | # Copyright (c) 2006, 2008 Junio C Hamano 4 | # 5 | # The "pre-rebase" hook is run just before "git rebase" starts doing 6 | # its job, and can prevent the command from running by exiting with 7 | # non-zero status. 8 | # 9 | # The hook is called with the following parameters: 10 | # 11 | # $1 -- the upstream the series was forked from. 12 | # $2 -- the branch being rebased (or empty when rebasing the current branch). 13 | # 14 | # This sample shows how to prevent topic branches that are already 15 | # merged to 'next' branch from getting rebased, because allowing it 16 | # would result in rebasing already published history. 17 | 18 | publish=next 19 | basebranch="$1" 20 | if test "$#" = 2 21 | then 22 | topic="refs/heads/$2" 23 | else 24 | topic=`git symbolic-ref HEAD` || 25 | exit 0 ;# we do not interrupt rebasing detached HEAD 26 | fi 27 | 28 | case "$topic" in 29 | refs/heads/??/*) 30 | ;; 31 | *) 32 | exit 0 ;# we do not interrupt others. 33 | ;; 34 | esac 35 | 36 | # Now we are dealing with a topic branch being rebased 37 | # on top of master. Is it OK to rebase it? 38 | 39 | # Does the topic really exist? 40 | git show-ref -q "$topic" || { 41 | echo >&2 "No such branch $topic" 42 | exit 1 43 | } 44 | 45 | # Is topic fully merged to master? 46 | not_in_master=`git rev-list --pretty=oneline ^master "$topic"` 47 | if test -z "$not_in_master" 48 | then 49 | echo >&2 "$topic is fully merged to master; better remove it." 50 | exit 1 ;# we could allow it, but there is no point. 51 | fi 52 | 53 | # Is topic ever merged to next? If so you should not be rebasing it. 54 | only_next_1=`git rev-list ^master "^$topic" ${publish} | sort` 55 | only_next_2=`git rev-list ^master ${publish} | sort` 56 | if test "$only_next_1" = "$only_next_2" 57 | then 58 | not_in_topic=`git rev-list "^$topic" master` 59 | if test -z "$not_in_topic" 60 | then 61 | echo >&2 "$topic is already up to date with master" 62 | exit 1 ;# we could allow it, but there is no point. 63 | else 64 | exit 0 65 | fi 66 | else 67 | not_in_next=`git rev-list --pretty=oneline ^${publish} "$topic"` 68 | /usr/bin/perl -e ' 69 | my $topic = $ARGV[0]; 70 | my $msg = "* $topic has commits already merged to public branch:\n"; 71 | my (%not_in_next) = map { 72 | /^([0-9a-f]+) /; 73 | ($1 => 1); 74 | } split(/\n/, $ARGV[1]); 75 | for my $elem (map { 76 | /^([0-9a-f]+) (.*)$/; 77 | [$1 => $2]; 78 | } split(/\n/, $ARGV[2])) { 79 | if (!exists $not_in_next{$elem->[0]}) { 80 | if ($msg) { 81 | print STDERR $msg; 82 | undef $msg; 83 | } 84 | print STDERR " $elem->[1]\n"; 85 | } 86 | } 87 | ' "$topic" "$not_in_next" "$not_in_master" 88 | exit 1 89 | fi 90 | 91 | <<\DOC_END 92 | 93 | This sample hook safeguards topic branches that have been 94 | published from being rewound. 95 | 96 | The workflow assumed here is: 97 | 98 | * Once a topic branch forks from "master", "master" is never 99 | merged into it again (either directly or indirectly). 100 | 101 | * Once a topic branch is fully cooked and merged into "master", 102 | it is deleted. If you need to build on top of it to correct 103 | earlier mistakes, a new topic branch is created by forking at 104 | the tip of the "master". This is not strictly necessary, but 105 | it makes it easier to keep your history simple. 106 | 107 | * Whenever you need to test or publish your changes to topic 108 | branches, merge them into "next" branch. 109 | 110 | The script, being an example, hardcodes the publish branch name 111 | to be "next", but it is trivial to make it configurable via 112 | $GIT_DIR/config mechanism. 113 | 114 | With this workflow, you would want to know: 115 | 116 | (1) ... if a topic branch has ever been merged to "next". Young 117 | topic branches can have stupid mistakes you would rather 118 | clean up before publishing, and things that have not been 119 | merged into other branches can be easily rebased without 120 | affecting other people. But once it is published, you would 121 | not want to rewind it. 122 | 123 | (2) ... if a topic branch has been fully merged to "master". 124 | Then you can delete it. More importantly, you should not 125 | build on top of it -- other people may already want to 126 | change things related to the topic as patches against your 127 | "master", so if you need further changes, it is better to 128 | fork the topic (perhaps with the same name) afresh from the 129 | tip of "master". 130 | 131 | Let's look at this example: 132 | 133 | o---o---o---o---o---o---o---o---o---o "next" 134 | / / / / 135 | / a---a---b A / / 136 | / / / / 137 | / / c---c---c---c B / 138 | / / / \ / 139 | / / / b---b C \ / 140 | / / / / \ / 141 | ---o---o---o---o---o---o---o---o---o---o---o "master" 142 | 143 | 144 | A, B and C are topic branches. 145 | 146 | * A has one fix since it was merged up to "next". 147 | 148 | * B has finished. It has been fully merged up to "master" and "next", 149 | and is ready to be deleted. 150 | 151 | * C has not merged to "next" at all. 152 | 153 | We would want to allow C to be rebased, refuse A, and encourage 154 | B to be deleted. 155 | 156 | To compute (1): 157 | 158 | git rev-list ^master ^topic next 159 | git rev-list ^master next 160 | 161 | if these match, topic has not merged in next at all. 162 | 163 | To compute (2): 164 | 165 | git rev-list master..topic 166 | 167 | if this is empty, it is fully merged to "master". 168 | 169 | DOC_END 170 | -------------------------------------------------------------------------------- /testdata/init-repo/hooks/pre-receive.sample: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # 3 | # An example hook script to make use of push options. 4 | # The example simply echoes all push options that start with 'echoback=' 5 | # and rejects all pushes when the "reject" push option is used. 6 | # 7 | # To enable this hook, rename this file to "pre-receive". 8 | 9 | if test -n "$GIT_PUSH_OPTION_COUNT" 10 | then 11 | i=0 12 | while test "$i" -lt "$GIT_PUSH_OPTION_COUNT" 13 | do 14 | eval "value=\$GIT_PUSH_OPTION_$i" 15 | case "$value" in 16 | echoback=*) 17 | echo "echo from the pre-receive-hook: ${value#*=}" >&2 18 | ;; 19 | reject) 20 | exit 1 21 | esac 22 | i=$((i + 1)) 23 | done 24 | fi 25 | -------------------------------------------------------------------------------- /testdata/init-repo/hooks/prepare-commit-msg.sample: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # 3 | # An example hook script to prepare the commit log message. 4 | # Called by "git commit" with the name of the file that has the 5 | # commit message, followed by the description of the commit 6 | # message's source. The hook's purpose is to edit the commit 7 | # message file. If the hook fails with a non-zero status, 8 | # the commit is aborted. 9 | # 10 | # To enable this hook, rename this file to "prepare-commit-msg". 11 | 12 | # This hook includes three examples. The first one removes the 13 | # "# Please enter the commit message..." help message. 14 | # 15 | # The second includes the output of "git diff --name-status -r" 16 | # into the message, just before the "git status" output. It is 17 | # commented because it doesn't cope with --amend or with squashed 18 | # commits. 19 | # 20 | # The third example adds a Signed-off-by line to the message, that can 21 | # still be edited. This is rarely a good idea. 22 | 23 | COMMIT_MSG_FILE=$1 24 | COMMIT_SOURCE=$2 25 | SHA1=$3 26 | 27 | /usr/bin/perl -i.bak -ne 'print unless(m/^. Please enter the commit message/..m/^#$/)' "$COMMIT_MSG_FILE" 28 | 29 | # case "$COMMIT_SOURCE,$SHA1" in 30 | # ,|template,) 31 | # /usr/bin/perl -i.bak -pe ' 32 | # print "\n" . `git diff --cached --name-status -r` 33 | # if /^#/ && $first++ == 0' "$COMMIT_MSG_FILE" ;; 34 | # *) ;; 35 | # esac 36 | 37 | # SOB=$(git var GIT_COMMITTER_IDENT | sed -n 's/^\(.*>\).*$/Signed-off-by: \1/p') 38 | # git interpret-trailers --in-place --trailer "$SOB" "$COMMIT_MSG_FILE" 39 | # if test -z "$COMMIT_SOURCE" 40 | # then 41 | # /usr/bin/perl -i.bak -pe 'print "\n" if !$first_line++' "$COMMIT_MSG_FILE" 42 | # fi 43 | -------------------------------------------------------------------------------- /testdata/init-repo/hooks/update.sample: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # 3 | # An example hook script to block unannotated tags from entering. 4 | # Called by "git receive-pack" with arguments: refname sha1-old sha1-new 5 | # 6 | # To enable this hook, rename this file to "update". 7 | # 8 | # Config 9 | # ------ 10 | # hooks.allowunannotated 11 | # This boolean sets whether unannotated tags will be allowed into the 12 | # repository. By default they won't be. 13 | # hooks.allowdeletetag 14 | # This boolean sets whether deleting tags will be allowed in the 15 | # repository. By default they won't be. 16 | # hooks.allowmodifytag 17 | # This boolean sets whether a tag may be modified after creation. By default 18 | # it won't be. 19 | # hooks.allowdeletebranch 20 | # This boolean sets whether deleting branches will be allowed in the 21 | # repository. By default they won't be. 22 | # hooks.denycreatebranch 23 | # This boolean sets whether remotely creating branches will be denied 24 | # in the repository. By default this is allowed. 25 | # 26 | 27 | # --- Command line 28 | refname="$1" 29 | oldrev="$2" 30 | newrev="$3" 31 | 32 | # --- Safety check 33 | if [ -z "$GIT_DIR" ]; then 34 | echo "Don't run this script from the command line." >&2 35 | echo " (if you want, you could supply GIT_DIR then run" >&2 36 | echo " $0 )" >&2 37 | exit 1 38 | fi 39 | 40 | if [ -z "$refname" -o -z "$oldrev" -o -z "$newrev" ]; then 41 | echo "usage: $0 " >&2 42 | exit 1 43 | fi 44 | 45 | # --- Config 46 | allowunannotated=$(git config --bool hooks.allowunannotated) 47 | allowdeletebranch=$(git config --bool hooks.allowdeletebranch) 48 | denycreatebranch=$(git config --bool hooks.denycreatebranch) 49 | allowdeletetag=$(git config --bool hooks.allowdeletetag) 50 | allowmodifytag=$(git config --bool hooks.allowmodifytag) 51 | 52 | # check for no description 53 | projectdesc=$(sed -e '1q' "$GIT_DIR/description") 54 | case "$projectdesc" in 55 | "Unnamed repository"* | "") 56 | echo "*** Project description file hasn't been set" >&2 57 | exit 1 58 | ;; 59 | esac 60 | 61 | # --- Check types 62 | # if $newrev is 0000...0000, it's a commit to delete a ref. 63 | zero="0000000000000000000000000000000000000000" 64 | if [ "$newrev" = "$zero" ]; then 65 | newrev_type=delete 66 | else 67 | newrev_type=$(git cat-file -t $newrev) 68 | fi 69 | 70 | case "$refname","$newrev_type" in 71 | refs/tags/*,commit) 72 | # un-annotated tag 73 | short_refname=${refname##refs/tags/} 74 | if [ "$allowunannotated" != "true" ]; then 75 | echo "*** The un-annotated tag, $short_refname, is not allowed in this repository" >&2 76 | echo "*** Use 'git tag [ -a | -s ]' for tags you want to propagate." >&2 77 | exit 1 78 | fi 79 | ;; 80 | refs/tags/*,delete) 81 | # delete tag 82 | if [ "$allowdeletetag" != "true" ]; then 83 | echo "*** Deleting a tag is not allowed in this repository" >&2 84 | exit 1 85 | fi 86 | ;; 87 | refs/tags/*,tag) 88 | # annotated tag 89 | if [ "$allowmodifytag" != "true" ] && git rev-parse $refname > /dev/null 2>&1 90 | then 91 | echo "*** Tag '$refname' already exists." >&2 92 | echo "*** Modifying a tag is not allowed in this repository." >&2 93 | exit 1 94 | fi 95 | ;; 96 | refs/heads/*,commit) 97 | # branch 98 | if [ "$oldrev" = "$zero" -a "$denycreatebranch" = "true" ]; then 99 | echo "*** Creating a branch is not allowed in this repository" >&2 100 | exit 1 101 | fi 102 | ;; 103 | refs/heads/*,delete) 104 | # delete branch 105 | if [ "$allowdeletebranch" != "true" ]; then 106 | echo "*** Deleting a branch is not allowed in this repository" >&2 107 | exit 1 108 | fi 109 | ;; 110 | refs/remotes/*,commit) 111 | # tracking branch 112 | ;; 113 | refs/remotes/*,delete) 114 | # delete tracking branch 115 | if [ "$allowdeletebranch" != "true" ]; then 116 | echo "*** Deleting a tracking branch is not allowed in this repository" >&2 117 | exit 1 118 | fi 119 | ;; 120 | *) 121 | # Anything else (is there anything else?) 122 | echo "*** Update hook: unknown type of update to ref $refname of type $newrev_type" >&2 123 | exit 1 124 | ;; 125 | esac 126 | 127 | # --- Finished 128 | exit 0 129 | -------------------------------------------------------------------------------- /testdata/init-repo/info/exclude: -------------------------------------------------------------------------------- 1 | # git ls-files --others --exclude-from=.git/info/exclude 2 | # Lines that start with '#' are comments. 3 | # For a project mostly in C, the following would be a good set of 4 | # exclude patterns (uncomment them if you want to use them): 5 | # *.[oa] 6 | # *~ 7 | -------------------------------------------------------------------------------- /testdata/init-repo/objects/2c/499787328348f09ae1e8f03757c6483b9a938a: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/goreleaser/chglog/b46d97bd57b12f8c0dc317dcd0568dff5db5b396/testdata/init-repo/objects/2c/499787328348f09ae1e8f03757c6483b9a938a -------------------------------------------------------------------------------- /testdata/init-repo/objects/2c/c00abc77d401a541d18c26e5c7fbef1effd3ed: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/goreleaser/chglog/b46d97bd57b12f8c0dc317dcd0568dff5db5b396/testdata/init-repo/objects/2c/c00abc77d401a541d18c26e5c7fbef1effd3ed -------------------------------------------------------------------------------- /testdata/init-repo/objects/3e/c1e9a60d07cc060cee727c97ffc8aac5713943: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/goreleaser/chglog/b46d97bd57b12f8c0dc317dcd0568dff5db5b396/testdata/init-repo/objects/3e/c1e9a60d07cc060cee727c97ffc8aac5713943 -------------------------------------------------------------------------------- /testdata/init-repo/objects/6f/59e5e770be445518775b22bbe25e89339b778b: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/goreleaser/chglog/b46d97bd57b12f8c0dc317dcd0568dff5db5b396/testdata/init-repo/objects/6f/59e5e770be445518775b22bbe25e89339b778b -------------------------------------------------------------------------------- /testdata/init-repo/objects/ab/515204e62e6fd2b468736de7aadcaca4d9dffd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/goreleaser/chglog/b46d97bd57b12f8c0dc317dcd0568dff5db5b396/testdata/init-repo/objects/ab/515204e62e6fd2b468736de7aadcaca4d9dffd -------------------------------------------------------------------------------- /testdata/init-repo/objects/e6/9de29bb2d1d6434b8b29ae775ad8c2e48c5391: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/goreleaser/chglog/b46d97bd57b12f8c0dc317dcd0568dff5db5b396/testdata/init-repo/objects/e6/9de29bb2d1d6434b8b29ae775ad8c2e48c5391 -------------------------------------------------------------------------------- /testdata/init-repo/objects/ea/2b12e15f99419047f49d5fc5dc844073899da8: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/goreleaser/chglog/b46d97bd57b12f8c0dc317dcd0568dff5db5b396/testdata/init-repo/objects/ea/2b12e15f99419047f49d5fc5dc844073899da8 -------------------------------------------------------------------------------- /testdata/init-repo/refs/heads/master: -------------------------------------------------------------------------------- 1 | 2c499787328348f09ae1e8f03757c6483b9a938a 2 | -------------------------------------------------------------------------------- /testdata/init-repo/refs/tags/v0.0.1: -------------------------------------------------------------------------------- 1 | 2c499787328348f09ae1e8f03757c6483b9a938a 2 | -------------------------------------------------------------------------------- /types.go: -------------------------------------------------------------------------------- 1 | // Package chglog contains the public API for working with a changlog.yml file 2 | package chglog 3 | 4 | import ( 5 | "time" 6 | 7 | "github.com/Masterminds/semver/v3" 8 | conventional_commit "gitlab.com/digitalxero/go-conventional-commit" 9 | ) 10 | 11 | // ChangeLogEntries list of ChangeLog entries. 12 | type ChangeLogEntries []*ChangeLog 13 | 14 | // Len returns the length of a collection. The number of Version instances 15 | // on the slice. 16 | func (c ChangeLogEntries) Len() int { 17 | return len(c) 18 | } 19 | 20 | // Less is needed for the sort interface to compare two Version objects on the 21 | // slice. If checks if one is less than the other. 22 | func (c ChangeLogEntries) Less(i, j int) bool { 23 | v1, err := semver.NewVersion(c[i].Semver) 24 | if err != nil { 25 | return true 26 | } 27 | v2, err := semver.NewVersion(c[j].Semver) 28 | if err != nil { 29 | return false 30 | } 31 | 32 | return v1.LessThan(v2) 33 | } 34 | 35 | // Swap is needed for the sort interface to replace the Version objects 36 | // at two different positions in the slice. 37 | func (c ChangeLogEntries) Swap(i, j int) { 38 | c[i], c[j] = c[j], c[i] 39 | } 40 | 41 | // PackageChangeLog used for the formatting API. 42 | type PackageChangeLog struct { 43 | Name string `yaml:"name"` 44 | Entries ChangeLogEntries `yaml:"entries"` 45 | } 46 | 47 | // ChangeLog a single changelog entry. 48 | type ChangeLog struct { 49 | ChangeLogOverridables `yaml:",inline"` 50 | Semver string `yaml:"semver"` 51 | Date time.Time `yaml:"date"` 52 | Packager string `yaml:"packager"` 53 | Notes *ChangeLogNotes `yaml:"notes,omitempty"` 54 | Changes ChangeLogChanges `yaml:"changes,omitempty"` 55 | } 56 | 57 | // ChangeLogOverridables contains potential format specific fields. 58 | type ChangeLogOverridables struct { 59 | Deb *ChangelogDeb `yaml:"deb,omitempty"` 60 | } 61 | 62 | // ChangelogDeb contains fields specific to the debian changelog format 63 | // https://www.debian.org/doc/debian-policy/ch-source.html#s-dpkgchangelog 64 | type ChangelogDeb struct { 65 | Urgency string `yaml:"urgency"` 66 | Distributions []string `yaml:"distributions"` 67 | } 68 | 69 | // ChangeLogNotes contains a potential header/footer string for output formatting. 70 | type ChangeLogNotes struct { 71 | Header *string `yaml:"header,omitempty"` 72 | Footer *string `yaml:"footer,omitempty"` 73 | } 74 | 75 | // ChangeLogChanges list of individual changes. 76 | type ChangeLogChanges []*ChangeLogChange 77 | 78 | // ChangeLogChange an individual change. 79 | type ChangeLogChange struct { 80 | Commit string `yaml:"commit"` 81 | Note string `yaml:"note"` 82 | // Author is the original author of the commit. 83 | Author *User `yaml:"author,omitempty"` 84 | // Committer is the one performing the commit, might be different from 85 | // Author. 86 | Committer *User `yaml:"committer,omitempty"` 87 | ConventionalCommit *conventional_commit.ConventionalCommit `yaml:"conventional_commit,omitempty"` 88 | } 89 | 90 | // User is used to identify who created a commit or tag. 91 | type User struct { 92 | // Name represents a person name. It is an arbitrary string. 93 | Name string `yaml:"name"` 94 | // Email is an email, but it cannot be assumed to be well-formed. 95 | Email string `yaml:"email"` 96 | } 97 | --------------------------------------------------------------------------------