1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
|
// Copyright 2019-2021 Luke Shumaker <lukeshu@parabola.nu>
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
// Command fi-prune-empty is a filter that prunes a fast-import
// stream, removing empty commits and empty merges.
package main
import (
"fmt"
"os"
"git.lukeshu.com/go/libfastimport"
"github.com/spf13/pflag"
"git.parabola.nu/~lukeshu/fastimport-go-utils/fiutil"
)
type Args struct {
help bool
srcdir string
pruneEmpty PruneArg
pruneFastForward PruneArg
existingReplace ExistingReplaceArg
newReplace NewReplaceArg
}
func main() {
var args Args
argparser := pflag.NewFlagSet(os.Args[0], pflag.ContinueOnError)
argparser.SortFlags = false
argparser.BoolVarP(&args.help, "help", "h", false, "Show this message")
argparser.StringVar(&args.srcdir, "srcdir", ".", "Path to the source git repository; this is required for '--prune-empty=auto' and '--prune-fastforward=auto'")
args.pruneEmpty = PruneAuto
argparser.Var(&args.pruneEmpty, "prune-empty", "Whether to prune empty commits; 'auto' means only prune commits which have become empty but are not empty in the --srcdir repo with GIT_NO_REPLACE_OBJECTS")
args.pruneFastForward = PruneAuto
argparser.Var(&args.pruneFastForward, "prune-fastforward", "Whether to prune merge commits that could be fast-forward merges; 'auto' means only prune merges which become could-be-fast-forward but are not could-be-fast-forward in the --srcdir repo with GIT_NO_REPLACE_OBJECTS")
args.existingReplace = ExistingReplaceDeleteOrUpdate
argparser.Var(&args.existingReplace, "existing-replace", "What to do with existing 'refs/replace/*' refs")
args.newReplace = NewReplaceAdd
argparser.Var(&args.newReplace, "new-replace", "Whether to create new 'refs/replace/*' refs")
err := argparser.Parse(os.Args[1:])
if args.help {
fmt.Printf("Usage: git fast-export --full-tree | %s [OPTIONS] | git fast-import\n", os.Args[0])
fmt.Println("A filter that prunes a fast-import stream, removing empty commits and empty merges")
fmt.Println()
fmt.Println("Requirements for the input stream:")
fmt.Println(" - Every commit must have a 'mark' (this is the default from `git fast-export`)")
fmt.Println(" - Every commit must have an 'original-oid' (eg: `git fast-export --show-original-ids`)")
fmt.Println(" if any of (note that several of these are the default):")
fmt.Println(" --prune-empty=auto")
fmt.Println(" --prune-fastforward=auto")
fmt.Println(" --existing-replace=keep-or-update")
fmt.Println(" --new-replace=add")
fmt.Println()
fmt.Println("OPTIONS:")
argparser.PrintDefaults()
os.Exit(0)
}
if err != nil {
fiutil.ErrUsage(err.Error())
}
if err := fiutil.NArgsExact(argparser, 0); err != nil {
fiutil.ErrUsage(err.Error())
}
frontend := libfastimport.NewFrontend(os.Stdin, os.Stdin, nil)
backend := libfastimport.NewBackend(os.Stdout, os.Stdout, nil)
filter := NewHandler(backend, NewDriver(args, backend))
if err := fiutil.RunHandler(frontend, filter); err != nil {
fmt.Fprintf(os.Stderr, "%s: error: %v\n", os.Args[0], err)
os.Exit(1)
}
}
type driver struct {
*Pruner
*Replacer
}
func (d driver) ProcessCommit(commit Commit) (*Commit, error) {
d.Replacer.ProcessCommit(commit)
return d.Pruner.ProcessCommit(commit)
}
func (d driver) GotMark(mark Mark, hash Hash) bool {
d.Replacer.GotMark(mark, hash)
return d.Pruner.GotMark(mark, hash)
}
func NewDriver(args Args, backend *libfastimport.Backend) Driver {
pruner := NewPruner(
args.pruneEmpty,
args.pruneFastForward,
args.srcdir,
backend)
replacer := NewReplacer(
args.existingReplace,
args.newReplace,
pruner,
backend)
return driver{
Pruner: pruner,
Replacer: replacer,
}
}
|