1GIT-FILTER-REPO(1)                Git Manual                GIT-FILTER-REPO(1)
2
3
4

NAME

6       git-filter-repo - Rewrite repository history
7

SYNOPSIS

9       git filter-repo --analyze
10       git filter-repo [<path_filtering_options>] [<content_filtering_options>]
11               [<ref_renaming_options>] [<commit_message_filtering_options>]
12               [<name_or_email_filtering_options>] [<parent_rewriting_options>]
13               [<generic_callback_options>] [<miscellaneous_options>]
14
15

DESCRIPTION

17       Rapidly rewrite entire repository history using user-specified filters.
18       This is a destructive operation which should not be used lightly; it
19       writes new commits, trees, tags, and blobs corresponding to (but
20       filtered from) the original objects in the repository, then deletes the
21       original history and leaves only the new. See the section called
22       “DISCUSSION” for more details on the ramifications of using this tool.
23       Several different types of history rewrites are possible; examples
24       include (but are not limited to):
25
26       ·   stripping large files (or large directories or large extensions)
27
28       ·   stripping unwanted files by path
29
30       ·   extracting wanted paths and their history (stripping everything
31           else)
32
33       ·   restructuring the file layout (such as moving all files into a
34           subdirectory in preparation for merging with another repo, making a
35           subdirectory become the new toplevel directory, or merging two
36           directories with independent filenames into one directory)
37
38       ·   renaming tags (also often in preparation for merging with another
39           repo)
40
41       ·   replacing or removing sensitive text such as passwords
42
43       ·   making mailmap rewriting of user names or emails permanent
44
45       ·   making grafts or replacement refs permanent
46
47       ·   rewriting commit messages
48
49       Additionally, several concerns are handled automatically (many of these
50       can be overridden, but they are all on by default):
51
52       ·   rewriting (possibly abbreviated) hashes in commit messages to refer
53           to the new post-rewrite commit hashes
54
55       ·   pruning commits which become empty due to the above filters (also
56           handles edge cases like pruning of merge commits which become
57           degenerate and empty)
58
59       ·   creating replace-refs (see git-replace(1)) for old commit hashes,
60           which if pushed and fetched will allow users to continue to refer
61           to new commits using (unabbreviated) old commit IDs
62
63       ·   stripping of original history to avoid mixing old and new history
64
65       ·   repacking the repository post-rewrite to shrink the repo for the
66           user
67
68       Also, it’s worth noting that there is an important safety mechanism:
69
70       ·   abort if run from a repo that is not a fresh clone (to prevent
71           accidental data loss from rewriting local history that doesn’t
72           exist anywhere else). See the section called “FRESH CLONE SAFETY
73           CHECK AND --FORCE”.
74
75       For those who know that there is large unwanted stuff in their history
76       and want help finding it, this command also
77
78       ·   provides an option to analyze a repository and generate reports
79           that can be useful in determining what to filter (or in determining
80           whether a separate filtering command was successful).
81
82       See also the section called “VERSATILITY”, the section called
83       “DISCUSSION”, the section called “EXAMPLES”, and the section called
84       “INTERNALS”.
85

OPTIONS

87   Analysis Options
88       --analyze
89           Analyze repository history and create a report that may be useful
90           in determining what to filter in a subsequent run (or in
91           determining if a previous filtering command did what you wanted).
92           Will not modify your repo.
93
94   Filtering based on paths (see also --filename-callback)
95       --invert-paths
96           Invert the selection of files from the specified
97           --path-{match,glob,regex} options below, i.e. only select files
98           matching none of those options.
99
100       --path-match <dir_or_file>, --path <dir_or_file>
101           Exact paths (files or directories) to include in filtered history.
102           Multiple --path options can be specified to get a union of paths.
103
104       --path-glob <glob>
105           Glob of paths to include in filtered history. Multiple --path-glob
106           options can be specified to get a union of paths.
107
108       --path-regex <regex>
109           Regex of paths to include in filtered history. Multiple
110           --path-regex options can be specified to get a union of paths.
111
112       --use-base-name
113           Match on file base name instead of full path from the top of the
114           repo. Incompatible with --path-rename, and incompatible with
115           matching against directory names.
116
117   Renaming based on paths (see also --filename-callback)
118       Note: if you combine path filtering with path renaming, be aware that a
119       rename directive does not select paths, it only says how to rename
120       paths that are selected with the filters.
121
122       --path-rename <old_name:new_name>, --path-rename-match
123       <old_name:new_name>
124           Path to rename; if filename or directory matches <old_name> rename
125           to <new_name>. Multiple --path-rename options can be specified.
126
127   Path shortcuts
128       --paths-from-file <filename>
129           Specify several path filtering and renaming directives, one per
130           line. Lines with ==> in them specify path renames, and lines can
131           begin with literal: (the default), glob:, or regex: to specify
132           different matching styles. Blank lines and lines starting with a #
133           are ignored (if you have a filename that you want to filter on that
134           starts with literal:, #, glob:, or regex:, then prefix the line
135           with literal:).
136
137       --subdirectory-filter <directory>
138           Only look at history that touches the given subdirectory and treat
139           that directory as the project root. Equivalent to using --path
140           <directory>/ --path-rename <directory>/:
141
142       --to-subdirectory-filter <directory>
143           Treat the project root as instead being under <directory>.
144           Equivalent to using --path-rename :<directory>/
145
146   Content editing filters (see also --blob-callback)
147       --replace-text <expressions_file>
148           A file with expressions that, if found, will be replaced. By
149           default, each expression is treated as literal text, but regex: and
150           glob: prefixes are supported. You can end the line with ==> and
151           some replacement text to choose a replacement choice other than the
152           default of ***REMOVED***.
153
154       --strip-blobs-bigger-than <size>
155           Strip blobs (files) bigger than specified size (e.g.  5M, 2G, etc)
156
157       --strip-blobs-with-ids <blob_id_filename>
158           Read git object ids from each line of the given file, and strip all
159           of them from history
160
161   Renaming of refs (see also --refname-callback)
162       --tag-rename <old:new>
163           Rename tags starting with <old> to start with <new>. For example,
164           --tag-rename foo:bar will rename tag foo-1.2.3 to bar-1.2.3; either
165           <old> or <new> can be empty.
166
167   Filtering of commit messages (see also --message-callback)
168       --preserve-commit-hashes
169           By default, since commits are rewritten and thus gain new hashes,
170           references to old commit hashes in commit messages are replaced
171           with new commit hashes (abbreviated to the same length as the old
172           reference). Use this flag to turn off updating commit hashes in
173           commit messages.
174
175       --preserve-commit-encoding
176           Do not reencode commit messages into UTF-8. By default, if the
177           commit object specifies an encoding for the commit message, the
178           message is re-encoded into UTF-8.
179
180   Filtering of names & emails (see also --name-callback and --email-callback)
181       --mailmap <filename>
182           Use specified mailmap file (see git-shortlog(1) for details on the
183           format) when rewriting author, committer, and tagger names and
184           emails. If the specified file is part of git history, historical
185           versions of the file will be ignored; only the current contents are
186           consulted.
187
188       --use-mailmap
189           Same as: --mailmap .mailmap
190
191   Parent rewriting
192       --replace-refs {delete-no-add, delete-and-add, update-no-add,
193       update-or-add, update-and-add}
194           Replace refs (see git-replace(1)) are used to rewrite parents
195           (unless turned off by the usual git mechanism); this flag specifies
196           what do do with those refs afterward. Replace refs can either be
197           deleted or updated to point at new commit hashes. Also, new replace
198           refs can be added for each commit rewrite. With update-or-add, new
199           replace refs are only added for commit rewrites that aren’t used to
200           update an existing replace ref. default is update-and-add if
201           $GIT_DIR/filter-repo/already_ran does not exist; update-or-add
202           otherwise.
203
204       --prune-empty {always, auto, never}
205           Whether to prune empty commits.  auto (the default) means only
206           prune commits which become empty (not commits which were empty in
207           the original repo, unless their parent was pruned). When the parent
208           of a commit is pruned, the first non-pruned ancestor becomes the
209           new parent.
210
211       --prune-degenerate {always, auto, never}
212           Since merge commits are needed for history topology, they are
213           typically exempt from pruning. However, they can become degenerate
214           with the pruning of other commits (having fewer than two parents,
215           having one commit serve as both parents, or having one parent as
216           the ancestor of the other.) If such merge commits have no file
217           changes, they can be pruned. The default (auto) is to only prune
218           empty merge commits which become degenerate (not which started as
219           such).
220
221       --no-ff
222           Even if the first parent is or becomes an ancestor of another
223           parent, do not prune it. This modifies how --prune-degenerate
224           behaves, and may be useful in projects who always use merge
225           --no-ff.
226
227   Generic callback code snippets
228       --filename-callback <function_body>
229           Python code body for processing filenames; see the section called
230           “CALLBACKS”.
231
232       --message-callback <function_body>
233           Python code body for processing messages (both commit messages and
234           tag messages); see the section called “CALLBACKS”.
235
236       --name-callback <function_body>
237           Python code body for processing names of people; see the section
238           called “CALLBACKS”.
239
240       --email-callback <function_body>
241           Python code body for processing emails addresses; see the section
242           called “CALLBACKS”.
243
244       --refname-callback <function_body>
245           Python code body for processing refnames; see the section called
246           “CALLBACKS”.
247
248       --blob-callback <function_body>
249           Python code body for processing blob objects; see the section
250           called “CALLBACKS”.
251
252       --commit-callback <function_body>
253           Python code body for processing commit objects; see the section
254           called “CALLBACKS”.
255
256       --tag-callback <function_body>
257           Python code body for processing tag objects; see the section called
258           “CALLBACKS”.
259
260       --reset-callback <function_body>
261           Python code body for processing reset objects; see the section
262           called “CALLBACKS”.
263
264   Location to filter from/to
265           Note
266           Specifying alternate source or target locations implies --partial
267           except that the normal default for --replace-refs is used. However,
268           unlike normal uses of --partial, this doesn’t risk mixing old and
269           new history since the old and new histories are in different
270           repositories.
271
272       --source <source>
273           Git repository to read from
274
275       --target <target>
276           Git repository to overwrite with filtered history
277
278   Miscellaneous options
279       --help, -h
280           Show a help message and exit.
281
282       --force, -f
283           Ignore fresh clone checks and rewrite history (an irreversible
284           operation, especially since it by default ends with an immediate
285           pruning of reflogs and old objects). See the section called “FRESH
286           CLONE SAFETY CHECK AND --FORCE”. Note that when cloning repos on a
287           local filesystem, it is better to pass --no-local to git clone than
288           passing --force to git-filter-repo.
289
290       --partial
291           Do a partial history rewrite, resulting in the mixture of old and
292           new history. This implies a default of update-no-add for
293           --replace-refs, disables rewriting refs/remotes/origin/* to
294           refs/heads/*, disables removing of the origin remote, disables
295           removing unexported refs, disables expiring the reflog, and
296           disables the automatic post-filter gc. Also, this modifies
297           --tag-rename and --refname-callback options such that instead of
298           replacing old refs with new refnames, it will instead create new
299           refs and keep the old ones around. Use with caution.
300
301       --refs <refs+>
302           Limit history rewriting to the specified refs. Implies --partial.
303           In addition to the normal caveats of --partial (mixing old and new
304           history, no automatic remapping of refs/remotes/origin/* to
305           refs/heads/*, etc.), this also may cause problems for pruning of
306           degenerate empty merge commits when negative revisions are
307           specified.
308
309       --dry-run
310           Do not change the repository. Run git fast-export and filter its
311           output, and save both the original and the filtered version for
312           comparison. This also disables rewriting commit messages due to not
313           knowing new commit IDs and disables filtering of some empty commits
314           due to inability to query the fast-import backend.
315
316       --debug
317           Print additional information about operations being performed and
318           commands being run. (If used together with --dry-run, shows extra
319           information about what would be run).
320
321       --stdin
322           Instead of running git fast-export and filtering its output, filter
323           the fast-export stream from stdin. The stdin must be in the
324           expected input format (e.g. it needs to include original-oid
325           directives).
326
327       --quiet
328           Pass --quiet to other git commands called.
329

OUTPUT

331       Every time filter-repo is run, files are created in the
332       .git/filter-repo/ directory. These files overwritten unconditionally on
333       every run.
334
335   Commit map
336       The .git/filter-repo/commit-map file contains a mapping of how all
337       commits were (or were not) changed.
338
339       ·   A header is the first line with the text "old" and "new"
340
341       ·   Commit mappings are in no particular order
342
343       ·   All commits in range of the rewrite will be listed, even commits
344           that are unchanged (e.g. because the commit pre-dated when the
345           large file(s) were introduced to the repo).
346
347       ·   An all-zeros hash, or null SHA, represents a non-existant object.
348           When in the "new" column, this means the commit was removed
349           entirely.
350
351   Reference map
352       The .git/filter-repo/ref-map file contains a mapping of which local
353       references were changed.
354
355       ·   A header is the first line with the text "old" and "new"
356
357       ·   Reference mappings are in no particular order
358
359       ·   An all-zeros hash, or null SHA, represents a non-existant object.
360           When in the "new" column, this means the ref was removed entirely.
361

FRESH CLONE SAFETY CHECK AND --FORCE

363       Since filter-repo does irreversible rewriting of history, it is
364       important to avoid making changes to a repo for which the user doesn’t
365       have a good backup. The primary defense mechanism is to simply educate
366       users and rely on them to be good stewards of their data; thus there
367       are several warnings in the documentation about how filter repo
368       rewrites history.
369
370       However, as a service to users, we would like to provide an additional
371       safety check beyond the documentation. There isn’t a good way to check
372       if the user has a good backup, but we can ask a related question that
373       is an imperfect but quite reasonable proxy: "Is this repository a fresh
374       clone?" Unfortunately, that is also a question we can’t get a perfect
375       answer to; git provides no way to answer that question. However, there
376       are approximately a dozen things that I found that seem to always be
377       true of brand new clones (assuming they are either clones of remote
378       repositories or are made with the --no-local flag), and I check for all
379       of those.
380
381       These checks can have both false positives and false negatives. Someone
382       might have a perfectly good backup of their repo without it actually
383       being a fresh clone — but there’s no way for filter-repo to know that.
384       Conversely, someone could look at all things that filter-repo checks
385       for in its safety checks and then just tweak their non-backed-up
386       repository to satisfy those conditions (though it would take a fair
387       amount of effort, and it’s astronomically unlikely that a repo that
388       isn’t a fresh clone randomly happens to match all the criteria). In
389       practice, the safety checks filter-repo uses seem to be really good at
390       avoiding people accidentally running filter-repo on a repository that
391       they shouldn’t be running it on. It even caught me once when I did mean
392       to run filter-repo but was in a different directory than I thought I
393       was.
394
395       In short, it’s perfectly fine to use ‘--force` to override the safety
396       checks as long as you’re okay with filter-repo irreversibly rewriting
397       the contents of the current repository. It is a really bad idea to get
398       in the habit of always specifying --force; if you do, one day you will
399       run one of your commands in the wrong directory like I did, and you
400       won’t have the safety check anymore to bail you out. Also, it is
401       definitely NOT okay to recommend --force on forums, Q&A sites, or in
402       emails to other users without first carefully explaining that --force
403       means putting your repositories’ data at risk. I am especially bothered
404       by people who suggest the flag when it clearly is NOT needed; they are
405       needlessly putting other peoples' data at risk.
406

VERSATILITY

408       filter-repo has a hierarchy of capabilities on the spectrum from easy
409       to use convenience flags that perform pre-defined types of filtering,
410       to choices that provide lots of flexibility in controlling how
411       filtering occurs. This spectrum includes the following:
412
413       ·   Convenience flags making common types of history rewriting simple
414           (e.g. --path, --strip-blobs-bigger-than, --replace-text, --mailmap)
415
416       ·   Options which are shorthand for others or which provide greater
417           control than others (e.g. --subdirectory-filter could just be
418           written using both a path selection (--path) and a path rename
419           (--path-rename) filter; --paths-from-file can handle all other
420           --path* options and more such as regex renaming of paths)
421
422       ·   Generic python callbacks for handling a certain type of data (the
423           filename, message, name, email, and refname callbacks)
424
425       ·   Generic python callbacks for handling fundamental git objects,
426           allowing greater control over the combination of data types the
427           object holds (the commit, tag, blob, and reset callbacks)
428
429       ·   The ability to import filter-repo as a module in a python program
430           and use its classes and functions for even greater control and
431           flexibility while still leveraging lots of basic capabilities. One
432           can even use this to write new tools with a completely different
433           interface.
434
435       For more information about callbacks, see the section called
436       “CALLBACKS”. For examples on writing python programs that import
437       filter-repo as a module to create new history rewriting tools, look at
438       the contrib/filter-repo-demos/ directory. That directory includes,
439       among other examples, a reimplementation of git-filter-branch which is
440       faster than git-filter-branch, and a reimplementation of BFG Repo
441       Cleaner with several bug fixes and new features.
442

DISCUSSION

444       Using filter-repo is relatively simple, but rewriting history is part
445       of a larger discussion in terms of collaboration. When you rewrite
446       history, the old and new histories are no longer compatible; if you
447       push this history somewhere for others to view, it will look as though
448       you’ve done a rebase of all branches and tags. Make sure you are
449       familiar with the "RECOVERING FROM UPSTREAM REBASE" section of git-
450       rebase(1) (and in particular, "The hard case") before proceeding, in
451       addition to this section.
452
453       Steps to use git-filter-repo as part of the bigger picture of doing a
454       history rewrite are roughly as follows:
455
456        1. Create a clone of your repository (if you created special refs
457           outside of refs/heads/ or refs/tags/, make sure to fetch those
458           too). You may pass --bare or --mirror to git clone, if you prefer.
459           You should pass --no-local if the repository you are cloning from
460           is on the local filesystem. Avoid other flags; some might confuse
461           the fresh clone check, and others could cause parts of the data to
462           be missing that are needed for the rewrite.
463
464        2. (Optional) Run git filter-repo --analyze. This will create a
465           directory of reports mentioning renames that have occurred in your
466           repo and also listing sizes of objects aggregated by
467           path/directory/extension/blob-id; this information may be useful in
468           choosing how to filter your repo. It can also be useful to re-run
469           --analyze after filtering to verify the changes look correct.
470
471        3. Run filter-repo with your desired filtering options. Many examples
472           are given below. For more complex cases, note that doing the
473           filtering in multiple steps (by running multiple filter-repo
474           invocations in a sequence) is supported. If anything goes wrong
475           here, simply delete your clone and restart.
476
477        4. Push your new repository to its new home (note that
478           refs/remotes/origin/* will have been moved to refs/heads/* as the
479           first part of filter-repo, so you can just deal with normal
480           branches instead of remote tracking branches). While you can force
481           push this to the same URL you cloned from, there are good reasons
482           to consider pushing to a different location instead:
483
484           ·   People who cloned from the original repo will have old history.
485               When they fetch the new history you force pushed up, unless
486               they do a git reset --hard @{u} on their branches or rebase
487               their local work, git will think they have hundreds or
488               thousands of commits with very similar commit messages as what
489               exist upstream (but which include files you wanted excised from
490               history), and allow the user to merge the two histories,
491               resulting in what looks like two copies of each commit. If they
492               then push this history back up, then everyone now has history
493               with two copies of each commit and the bad files have returned.
494               You’re more likely to succeed in forcing people to get rid of
495               the old history if they have to clone a new URL.
496
497           ·   Rewriting history will rewrite tags; those who have already
498               downloaded tags will not get the updated tags by default (see
499               the "On Re-tagging" section of git-tag(1)). Every user trying
500               to use an existing clone will have to forcibly delete all tags
501               and re-fetch them; it may be easier for them to just re-clone,
502               which they are more likely to do with a new clone URL.
503
504           ·   Rewriting history may delete some refs (e.g. branches that only
505               had files that you wanted excised from history); unless you run
506               git push with the --mirror or --prune options, those refs will
507               continue to exist on the server. If folks then merge these
508               branches into others, then people have started mixing old and
509               new history. If users had already cloned these branches,
510               removing them from the server isn’t enough; you need all users
511               to delete any local branches based on these refs and run fetch
512               with the --prune option as well. Simply re-cloning from a new
513               URL is easier.
514
515           ·   The server may not allow you to force push over some refs. For
516               example, code review systems may have special ref namespaces
517               (e.g. refs/changes/, refs/pull/, refs/merge-requests/) that
518               they have locked down.
519
520        5. If you still want to push your rewritten history back to the
521           original url despite my warnings above, you’ll have to manage it
522           very carefully:
523
524           ·   git-filter-repo deletes the "origin" remote to help avoid
525               people accidentally repushing to the same repository, so you’ll
526               need to remind git what origin’s url was. You’ll have to look
527               up the command for that.
528
529           ·   You’ll need to carefully synchronize with everyone who has
530               cloned the repository, and will also need to carefully
531               synchronize with everything (e.g. CI systems) that has cloned
532               it. Every single clone will either need to be thrown away and
533               re-cloned, or need to take all the steps outlined in item 4 as
534               well as follow the necessary steps from "RECOVERING FROM
535               UPSTREAM REBASE" section of git-rebase(1). If you miss fixing
536               any clones, you’ll risk mixing old and new history and end up
537               with an even worse mess to clean up.
538
539           ·   Finally, you’ll need to consult any documentation from your
540               hosting provider about how to remove any server-side references
541               to the old commits (example: GitLab’s docs on reducing
542               repository size[1]).
543
544        6. (Optional) Some additional considerations
545
546           ·   filter-repo by default creates replace refs (see git-
547               replace(1)) for each rewritten commit ID, allowing you to use
548               old (unabbreviated) commit hashes to refer to the newly
549               rewritten commits. If you want to use these replace refs, push
550               them to the relevant clone URL and tell users to adjust their
551               fetch refspec (e.g.  git config --add remote.origin.fetch
552               +refs/replace/*:refs/replace/*) Sadly, some existing git
553               servers (e.g. Gerrit, GitHub) do not yet understand replace
554               refs, and thus one can’t use old commit hashes within their UI;
555               this may change in the future. But replace refs at least help
556               users locally within the git CLI.
557
558           ·   If you have a central repo, you may want to prevent people from
559               pushing old commit IDs, in order to avoid mixing old and new
560               history. Every repository manager does this differently, some
561               provide specialized commands (e.g.
562               https://gerrit-review.googlesource.com/Documentation/cmd-ban-commit.html),
563               others require you to write hooks.
564

EXAMPLES

566   Path based filtering
567       To only keep the README.md file plus the directories guides and
568       tools/releases/:
569
570           git filter-repo --path README.md --path guides/ --path tools/releases
571
572
573       Directory names can be given with or without a trailing slash, and all
574       filenames are relative to the toplevel of the repo. To keep all files
575       except these paths, just add --invert-paths:
576
577           git filter-repo --path README.md --path guides/ --path tools/releases --invert-paths
578
579
580       If you want to have both an inclusion filter and an exclusion filter,
581       just run filter-repo multiple times. For example, to keep the src/main
582       subdirectory but exclude files under src/main named data, run:
583
584           git filter-repo --path src/main/
585           git filter-repo --path-glob 'src/*/data' --invert-paths
586
587
588       Note that the asterisk (*) will match across multiple directories, so
589       the second command would remove e.g. src/main/org/whatever/data. Also,
590       the second command by itself would also remove e.g.
591       src/not-main/foo/data, but since src/not-main/ was removed by the first
592       command, that’s not an issue. Also, the use of quotes around the
593       asterisk is sometimes important to avoid glob expansion by the shell.
594
595       You can also select paths by regular expression (see
596       https://docs.python.org/3/library/re.html#regular-expression-syntax).
597       For example, to only include files from the repo whose name is in the
598       format YYYY-MM-DD.txt and is found at least two subdirectories deep:
599
600           git filter-repo --path-regex '^.*/.*/[0-9]{4}-[0-9]{2}-[0-9]{2}.txt$'
601
602
603       If you want two directories to be renamed (and maybe merged if both are
604       renamed to the same location), use --path-rename; for example, to
605       rename both cmds/ and src/scripts/ to tools/:
606
607           git filter-repo --path-rename cmds:tools --path-rename src/scripts/:tools/
608
609
610       As with --path, directories can be specified with or without a trailing
611       slash for --path-rename.
612
613       If you do a --path-rename to something that was already in use, it will
614       be silently overwritten. However, if you try to rename multiple files
615       to the same location (e.g. src/scripts/run_release.sh and
616       cmds/run_release.sh both existed and had different content with the
617       renames above), then you will be given an error. If you have such a
618       case, you may want to add another rename command to move one of the
619       paths somewhere else where it won’t collide:
620
621           git filter-repo --path-rename cmds/run_release.sh:tools/do_release.sh \
622                           --path-rename cmds/:tools/ \
623                           --path-rename src/scripts/:tools/
624
625
626       Also, --path-rename brings up ordering issues; all path arguments are
627       applied in order. Thus, a command like
628
629           git filter-repo --path-rename sources/:src/main/ --path src/main/
630
631
632       would make sense but reversing the two arguments would not (src/main/
633       is created by the rename so reversing the two would give you an empty
634       repo). Also, note that the rename of cmds/run_release.sh a couple
635       examples ago was done before the other renames.
636
637       Note that path renaming does not do path filtering, thus the following
638       command
639
640           git filter-repo --path src/main/ --path-rename tools/:scripts/
641
642
643       would not result in the tools or scripts directories being present,
644       because the single filter selected only src/main/. It’s likely that you
645       would instead want to run:
646
647           git filter-repo --path src/main/ --path tools/ --path-rename tools/:scripts/
648
649
650       If you prefer to filter based solely on basename, use the
651       --use-base-name flag (though this is incompatible with --path-rename).
652       For example, to only include README.md and Makefile files from any
653       directory:
654
655           git filter-repo --use-base-name --path README.md --path Makefile
656
657
658       If you wanted to delete all .DS_Store files in any directory, you could
659       either use:
660
661           git filter-repo --invert-paths --path '.DS_Store' --use-base-name
662
663
664       or
665
666           git filter-repo --invert-paths --path-glob '*/.DS_Store' --path '.DS_Store'
667
668
669       (the --path-glob isn’t sufficient by itself as it might miss a toplevel
670       .DS_Store file; further while something like --path-glob '*.DS_Store'
671       would workaround that problem it would also grab files named
672       foo.DS_Store or bar/baz.DS_Store)
673
674       Finally, see also the --filename-callback from the section called
675       “CALLBACKS”.
676
677   Filtering based on many paths
678       If you have a long list of files, directories, globs, or regular
679       expressions to filter on, you can stick them in a file and use
680       --paths-from-file; for example, with a file named stuff-i-want.txt with
681       contents of
682
683           # Blank lines and comment lines are ignored.
684           # Examples similar to --path:
685           README.md
686           guides/
687           tools/releases
688
689           # An example that is like --path-glob:
690           glob:*.py
691
692           # An example that is like --path-regex:
693           regex:^.*/.*/[0-9]{4}-[0-9]{2}-[0-9]{2}.txt$
694
695           # An example of renaming a path
696           tools/==>scripts/
697
698           # An example of using a regex to rename a path
699           regex:(.*)/([^/]*)/([^/]*)\.text$==>\2/\1/\3.txt
700
701
702       then you could run
703
704           git filter-repo --paths-from-file stuff-i-want.txt
705
706
707       to get a repo containing only the toplevel README.md file, the guides/
708       and tools/releases/ directories, all python files, files whose name was
709       of the form YYYY.MM-DD.txt at least two subdirectories deep, and would
710       rename tools/ to scripts/ and rename files like foo/bar/baz.text to
711       bar/foo/baz.txt. Note the special line prefixes of glob: and regex: and
712       the special string ==> denoting renames.
713
714       Sometimes you have a way of easily generating all the files you want.
715       For example, if you know that none of the currently tracked files have
716       any newlines or special characters in them (see core.quotePath from git
717       config --help) so that git ls-files would print all files literally one
718       per line, and you knew that you wanted to keep only the files that are
719       currently tracked (thus deleting from all commits in history any files
720       that only appear on other branches or that only appear in older
721       commits), then you could use a pair of commands such as
722
723           git ls-files >../paths-i-want.txt
724           git filter-repo --paths-from-file ../paths-i-want.txt
725
726
727       Similarly, you could use --paths-from-file to delete many files. For
728       example, you could run git filter-repo --analyze to get reports, look
729       in one such as .git/filter-repo/analysis/path-deleted-sizes.txt and
730       copy all the filenames into a file such as
731       /tmp/files-i-dont-want-anymore.txt and then run
732
733           git filter-repo --invert-paths --paths-from-file /tmp/files-i-dont-want-anymore.txt
734
735
736       to delete them all.
737
738   Directory based shortcuts
739       Let’s say you had a directory structure like the following:
740
741           module/
742              foo.c
743              bar.c
744           otherDir/
745              blah.config
746              stuff.txt
747           zebra.jpg
748
749       If you wanted just the module/ directory and you wanted it to become
750       the new root so that your new directory structure looked like
751
752           foo.c
753           bar.c
754
755       then you could run:
756
757           git filter-repo --subdirectory-filter module/
758
759
760       If you wanted all the files from the original repo, but wanted to move
761       everything under a subdirectory named my-module/, so that your new
762       directory structure looked like
763
764           my-module/
765              module/
766                 foo.c
767                 bar.c
768              otherDir/
769                 blah.config
770                 stuff.txt
771              zebra.jpg
772
773       then you would instead run run
774
775           git filter-repo --to-subdirectory-filter my-module/
776
777
778   Content based filtering
779       If you want to filter out all files bigger than a certain size, you can
780       use --strip-blobs-bigger-than with some size (K, M, and G suffixes are
781       recognized), e.g.:
782
783           git filter-repo --strip-blobs-bigger-than 10M
784
785
786       If you want to strip out all files with specified git object ids
787       (hashes), list the hashes in a file and run
788
789           git filter-repo --strip-blobs-with-ids FILE_WITH_GIT_BLOB_IDS
790
791
792       If you want to modify file contents, you can do so based on a list of
793       expressions in a file, one per line. For example, with a file named
794       expressions.txt containing
795
796           p455w0rd
797           foo==>bar
798           glob:*666*==>
799           regex:\bdriver\b==>pilot
800           literal:MM/DD/YYYY==>YYYY-MM-DD
801           regex:([0-9]{2})/([0-9]{2})/([0-9]{4})==>\3-\1-\2
802
803
804       then running
805
806           git filter-repo --replace-text expressions.txt
807
808
809       will go through and replace p455w0rd with ***REMOVED***, foo with bar,
810       any line containing 666 with a blank line, the word driver with pilot
811       (but not if it has letters before or after; e.g. drivers will be
812       unmodified), replace the exact text MM/DD/YYYY with YYYY-MM-DD and
813       replace date strings of the form MM/DD/YYYY with ones of the form
814       YYYY-MM-DD. In the expressions file, there are a few things to note:
815
816       ·   Every line has a replacement, given by whatever is on the right of
817           ==>. If ==> does not appear on the line, the default replacement is
818           ***REMOVED***.
819
820       ·   Lines can start with literal:, glob:, or regex: to specify whether
821           to do literal string matches, globs (see
822           https://docs.python.org/3/library/fnmatch.html), or regular
823           expressions (see
824           https://docs.python.org/3/library/re.html#regular-expression-syntax).
825           If none of these are specified, literal: is assumed.
826
827       ·   If multiple matches are found, all are replaced.
828
829       ·   globs and regexes are applied to the entire file, but without any
830           special flags turned on. Some folks may be interested in adding
831           (?m) to the regex to turn on MULTILINE mode, so that ^ and $ match
832           the beginning and ends of lines rather than the beginning and end
833           of file. See https://docs.python.org/3/library/re.html for details.
834
835       See also the --blob-callback from the section called “CALLBACKS”.
836
837   Refname based filtering
838       To rename tags, use --tag-rename, e.g.:
839
840           git filter-repo --tag-rename foo:bar
841
842
843       This will rename any tags starting with foo to now start with bar.
844       Either side of the colon could be blank, e.g.
845
846           git filter-repo --tag-rename '':'my-module-'
847
848
849       For more general refname modification, see --refname-callback from the
850       section called “CALLBACKS”.
851
852   User and email based filtering
853       To modify username and emails of commits, you can create a mailmap file
854       in the format accepted by git-shortlog(1). For example, if you have a
855       file named my-mailmap you can run
856
857           git filter-repo --mailmap my-mailmap
858
859
860       and if the current contents of that file are as follows (if the
861       specified mailmap file is version controlled, historical versions of
862       the file are ignored):
863
864           Name For User <email@addre.ss>
865           <new@ema.il> <old1@ema.il>
866           New Name And <new@ema.il> <old2@ema.il>
867           New Name And <new@ema.il> Old Name And <old3@ema.il>
868
869
870       then we can update username and/or emails based on the specified
871       mapping.
872
873       See also the --name-callback and --email-callback from the section
874       called “CALLBACKS”.
875
876   Parent rewriting
877       To replace $commit_A with $commit_B (e.g. make all commits which had
878       $commit_A as a parent instead have $commit_B for that parent), and
879       rewrite history to make it permanent:
880
881           git replace $commit_A $commit_B
882           git filter-repo --force
883
884
885       To create a new commit with the same contents as $commit_A except with
886       different parent(s) and then replace $commit_A with the new commit, and
887       rewrite history to make it permanent:
888
889           git replace --graft $commit_A $new_parent_or_parents
890           git filter-repo --force
891
892
893       The reason to specify --force is two-fold: filter-repo will error out
894       if no arguments are specified, and the new graft commit would otherwise
895       trigger the not-a-fresh-clone check.
896
897   Partial history rewrites
898       To rewrite the history on just one branch (which may cause it to no
899       longer share any common history with other branches), use --refs. For
900       example, to remove a file named extraneous.txt from the master branch:
901
902           git filter-repo --invert-paths --path extraneous.txt --refs master
903
904
905       To rewrite just some recent commits:
906
907           git filter-repo --invert-paths --path extraneous.txt --refs master~3..master
908
909

CALLBACKS

911       For flexibility, filter-repo allows you to specify functions on the
912       command line to further filter all changes. Please note that there are
913       some API compatibility caveats associated with these callbacks that you
914       should be aware of before using them; see the "API BACKWARD
915       COMPATIBILITY CAVEAT" comment near the top of git-filter-repo source
916       code.
917
918       All callback functions are of the same general format. For a command
919       line argument like
920
921           --foo-callback 'BODY'
922
923
924       the following code will be compiled and called:
925
926           def foo_callback(foo):
927             BODY
928
929
930       Thus, you just need to make sure your BODY modifies and returns foo
931       appropriately. One important thing to note for all callbacks is that
932       filter-repo uses bytestrings (see
933       https://docs.python.org/3/library/stdtypes.html#bytes) everywhere
934       instead of strings.
935
936       There are four callbacks that allow you to operate directly on raw
937       objects that contain data that’s easy to write in fast-import(1)
938       format:
939
940           --blob-callback
941           --commit-callback
942           --tag-callback
943           --reset-callback
944
945
946       We’ll come back to these later because it is often the case that the
947       other callbacks are more convenient. The other callbacks operate on a
948       small piece of the raw objects or operate on pieces across multiple
949       types of raw object (e.g. author names and committer names and tagger
950       names across commits and tags, or refnames across commits, tags, and
951       resets, or messages across commits and tags). The convenience callbacks
952       are:
953
954           --filename-callback
955           --message-callback
956           --name-callback
957           --email-callback
958           --refname-callback
959
960
961       in each you are expected to simply return a new value based on the one
962       passed in. For example,
963
964           git-filter-repo --name-callback 'return name.replace(b"Wiliam", b"William")'
965
966
967       would result in the following function being called:
968
969           def name_callback(name):
970             return name.replace(b"Wiliam", b"William")
971
972
973       The email callback is quite similar:
974
975           git-filter-repo --email-callback 'return email.replace(b".cm", b".com")'
976
977
978       The refname callback is also similar, but note that the refname passed
979       in and returned are expected to be fully qualified (e.g.
980       b"refs/heads/master" instead of just b"master" and b"refs/tags/v1.0.7"
981       instead of b"1.0.7"):
982
983           git-filter-repo --refname-callback '
984             # Change e.g. refs/heads/master to refs/heads/prefix-master
985             rdir,rpath = os.path.split(refname)
986             return rdir + b"/prefix-" + rpath'
987
988
989       The message callback is quite similar to the previous three callbacks,
990       though it operates on a bytestring that is likely more than one line:
991
992           git-filter-repo --message-callback '
993             if b"Signed-off-by:" not in message:
994               message += b"\nSigned-off-by: Me My <self@and.eye>"
995             return re.sub(b"[Ee]-?[Mm][Aa][Ii][Ll]", b"email", message)'
996
997
998       The filename callback is slightly more interesting. Returning None
999       means the file should be removed from all commits, returning the
1000       filename unmodified marks the file to be kept, and returning a
1001       different name means the file should be renamed. An example:
1002
1003           git-filter-repo --filename-callback '
1004             if b"/src/" in filename:
1005               # Remove all files with a directory named "src" in their path
1006               # (except when "src" appears at the toplevel).
1007               return None
1008             elif filename.startswith(b"tools/"):
1009               # Rename tools/ -> scripts/misc/
1010               return b"scripts/misc/" + filename[6:]
1011             else:
1012               # Keep the filename and do not rename it
1013               return filename
1014             '
1015
1016
1017       In contrast, the blob, reset, tag, and commit callbacks are not
1018       expected to return a value, but are instead expected to modify the
1019       object passed in. Major fields for these objects are (subject to API
1020       backward compatibility caveats mentioned previously):
1021
1022       ·   Blob: original_id (original hash) and data
1023
1024       ·   Reset: ref (name of reference) and from_ref (hash or integer mark)
1025
1026       ·   Tag: ref, from_ref, original_id, tagger_name, tagger_email,
1027           tagger_date, message
1028
1029       ·   Commit: branch, original_id, author_name, author_email,
1030           author_date, committer_name, committer_email, committer_date,
1031           message, file_changes (list of FileChange objects, each containing
1032           a type, filename, mode, and blob_id), parents (list of hashes or
1033           integer marks)
1034
1035       An example of each:
1036
1037           git filter-repo --blob-callback '
1038             if len(blob.data) > 25:
1039               # Mark this blob for removal from all commits
1040               blob.skip()
1041             else:
1042               blob.data = blob.data.replace(b"Hello", b"Goodbye")
1043             '
1044
1045
1046
1047           git filter-repo --reset-callback 'reset.ref = reset.ref.replace(b"master", b"dev")'
1048
1049
1050
1051           git filter-repo --tag-callback '
1052             if tag.tagger_name == b"Jim Williams":
1053               # Omit this tag
1054               tag.skip()
1055             else:
1056               tag.message = tag.message + b"\n\nTag of %s by %s on %s" % (tag.ref, tag.tagger_email, tag.tagger_date)'
1057
1058
1059
1060           git filter-repo --commit-callback '
1061             # Remove executable files with three 6s in their name (including
1062             # from leading directories).
1063             # Also, undo deletion of sources/foo/bar.txt (change types are
1064             # either b"D" (deletion) or b"M" (add or modify); renames are
1065             # handled by deleting the old file and adding a new one)
1066             commit.file_changes = [
1067                    change for change in commit.file_changes
1068                    if not (change.mode == b"100755" and
1069                            change.filename.count(b"6") == 3) and
1070                       not (change.type == b"D" and
1071                            change.filename == b"sources/foo/bar.txt")]
1072             # Mark all .sh files as executable; modes in git are always one of
1073             # 100644 (normal file), 100755 (executable), 120000 (symlink), or
1074             # 160000 (submodule)
1075             for change in commit.file_changes:
1076               if change.filename.endswith(b".sh"):
1077                 change.mode = b"100755"
1078             '
1079
1080

INTERNALS

1082       You probably don’t need to read this section unless you are just very
1083       curious or you are trying to do a very complex history rewrite.
1084
1085   How filter-repo works
1086       Roughly, filter-repo works by running
1087
1088           git fast-export <options> | filter | git fast-import <options>
1089
1090
1091       where filter-repo not only launches the whole pipeline but also serves
1092       as the filter in the middle. However, filter-repo does a few additional
1093       things on top in order to make it into a well-rounded filtering tool. A
1094       sequence that more accurately reflects what filter-repo runs is:
1095
1096        1. Verify we’re in a fresh clone
1097
1098        2. git fetch -u . refs/remotes/origin/*:refs/heads/*
1099
1100        3. git remote rm origin
1101
1102        4. git fast-export --show-original-ids --reference-excluded-parents
1103           --fake-missing-tagger --signed-tags=strip
1104           --tag-of-filtered-object=rewrite --use-done-feature --no-data
1105           --reencode=yes --mark-tags --all | filter | git -c
1106           core.ignorecase=false fast-import --date-format=raw-permissive
1107           --force --quiet
1108
1109        5. git update-ref --no-deref --stdin, fed with a list of refs to nuke,
1110           and a list of replace refs to delete, create, or update.
1111
1112        6. git reset --hard
1113
1114        7. git reflog expire --expire=now --all
1115
1116        8. git gc --prune=now
1117
1118       Some notes or exceptions on each of the above:
1119
1120        1. If we’re not in a fresh clone, users will not be able to recover if
1121           they used the wrong command or ran in the wrong repo. (Though
1122           --force overrides this check, and it’s also off if you’ve already
1123           ran filter-repo once in this repo.)
1124
1125        2. Technically, we actually use a git update-ref command fed with a
1126           lot of input due to the fact that users can use --force when local
1127           branches might not match remote branches. But this fetch command
1128           catches the intent rather succinctly.
1129
1130        3. We don’t want users accidentally pushing back to the original repo,
1131           as discussed in the section called “DISCUSSION”. It also reminds
1132           users that since history has been rewritten, this repo is no longer
1133           compatible with the original. Finally, another minor benefit is
1134           this allows users to push with the --mirror option to their new
1135           home without accidentally sending remote tracking branches.
1136
1137        4. Some of these flags are always used but others are actually
1138           conditional. For example, filter-repo’s --replace-text and
1139           --blob-callback options need to work on blobs so --no-data cannot
1140           be passed to fast-export. But when we don’t need to work on blobs,
1141           passing --no-data speeds things up. Also, other flags may change
1142           the structure of the pipeline as well (e.g.  --dry-run and --debug)
1143
1144        5. We use this step to write replace refs for accessing the newly
1145           written commit hashes using their previous names. Also, if refs
1146           were renamed by various steps, we need to delete the old refnames
1147           in order to avoid mixing old and new history.
1148
1149        6. Users also have old versions of files in their working tree and
1150           index; we want those cleaned up to match the rewritten history as
1151           well. Note that this step is skipped in bare repos.
1152
1153        7. Reflogs will hold on to old history, so we need to expire them.
1154
1155        8. We need to gc to avoid mixing new and old history. Also, it shrinks
1156           the repository for users, so they don’t have to do extra work.
1157           (Odds are that they’ve only rewritten trees and commits and maybe a
1158           few blobs, so --aggressive isn’t needed and would be too slow.)
1159
1160       Information about these steps is printed out when --debug is passed to
1161       filter-repo. When doing a --partial history rewrite, steps 2, 3, 7, and
1162       8 are unconditionally skipped, step 5 is skipped if --replace-refs is
1163       update-no-add, and just the nuke-unused-refs portion of step 5 is
1164       skipped if --replace-refs is something else.
1165
1166   Limitations
1167       Inherited limitations
1168           Since git filter-repo calls fast-export and fast-import to do a lot
1169           of the heavy lifting, it inherits limitations from those systems:
1170
1171           ·   extended commit headers, if any, are stripped
1172
1173           ·   commits get rewritten meaning they will have new hashes;
1174               therefore, signatures on commits and tags cannot continue to
1175               work and instead are just removed (thus signed tags become
1176               annotated tags)
1177
1178           ·   tags of commits are supported. Prior to git-2.24.0, tags of
1179               blobs and tags of tags are not supported (fast-export would die
1180               on such tags). tags of trees are not supported in any git
1181               version (since fast-export ignores tags of trees with a warning
1182               and fast-import provides no way to import them).
1183
1184           ·   annotated and signed tags outside of the refs/tags/ namespace
1185               are not supported (their location will be mangled in weird
1186               ways)
1187
1188           ·   fast-import will die on various forms of invalid input, such as
1189               a timezone with more than four digits
1190
1191           ·   fast-export cannot reencode commit messages into UTF-8 if the
1192               commit message is not valid in its specified encoding (in such
1193               cases, it’ll leave the commit message and the encoding header
1194               alone).
1195
1196           ·   commits without an author will be given one matching the
1197               committer
1198
1199           ·   tags without a tagger will be given a fake tagger
1200
1201           ·   references that include commit cycles in their history (which
1202               can be created with git-replace(1)) will not be flagged to the
1203               user as an error but will be silently deleted by fast-export as
1204               though the branch or tag contained no interesting files
1205
1206           There are also some limitations due to the design of these systems:
1207
1208           ·   Trying to insert additional files into the stream can be
1209               tricky; since fast-export only lists file changes in a merge
1210               relative to its first parent, if you insert additional files
1211               into a commit that is in the second (or third or fourth) parent
1212               history of a merge, then you also need to add it to the merge
1213               manually. (Similarly, if you change which parent is the first
1214               parent in a merge commit, you need to manually update the list
1215               of file changes to be relative to the new first parent.)
1216
1217           ·   fast-export and fast-import work with exact file contents, not
1218               patches. (e.g. "Whatever the current contents of this file,
1219               update them to now have these contents") Because of this,
1220               removing the changes made in a single commit or inserting
1221               additional changes to a file in some commit and expecting them
1222               to propagate forward is not something that can be done with
1223               these tools. Use git-rebase(1) for that.
1224
1225       Intrinsic limitations
1226           Some types of filtering have limitations that would affect any tool
1227           attempting to perform them; the most any tool can do is attempt to
1228           notify the user when it detects an issue:
1229
1230           ·   When rewriting commit hashes in commit messages, there are a
1231               variety of cases when the hash will not be updated (whenever
1232               this happens, a note is written to
1233               .git/filter-repo/suboptimal-issues):
1234
1235               ·   if a commit hash does not correspond to a commit in the old
1236                   repo
1237
1238               ·   if a commit hash corresponds to a commit that gets pruned
1239
1240               ·   if an abbreviated hash is not unique
1241
1242           ·   Pruning of empty commits can cause a merge commit to lose an
1243               entire ancestry line and become a non-merge. If the merge
1244               commit had no changes then it can be pruned too, but if it
1245               still has changes it needs to be kept. This might cause minor
1246               confusion since the commit will likely have a commit message
1247               that makes it sound like a merge commit even though it’s not.
1248               (Whenever a merge commit becomes a non-merge commit, a note is
1249               written to .git/filter-repo/suboptimal-issues)
1250
1251       Issues specific to filter-repo
1252           ·   Multiple repositories in the wild have been observed which use
1253               a bogus timezone (+051800); google will find you some reports.
1254               The intended timezone wasn’t clear or wasn’t always the same.
1255               Replace with a different bogus timezone that fast-import will
1256               accept (+0261).
1257
1258           ·   --path-rename can result in pathname collisions; to avoid
1259               excessive memory requirements of tracking which files are in
1260               all commits or looking up what files exist with either every
1261               commit or every usage of --path-rename, we just tell the user
1262               that they might clobber other changes if they aren’t careful.
1263               We can check if the clobbering comes from another --path-rename
1264               without much overhead. (Perhaps in the future it’s worth adding
1265               a slow mode to --path-rename that will do the more exhaustive
1266               checks?)
1267
1268           ·   There is no mechanism for directly controlling which flags are
1269               passed to fast-export (or fast-import); only pre-defined flags
1270               can be turned on or off as a side-effect of other options.
1271               Direct control would make little sense because some options
1272               like --full-tree would require additional code in filter-repo
1273               (to parse new directives), and others such as -M or -C would
1274               break assumptions used in other places of filter-repo.
1275
1276           ·   Partial-repo filtering, while supported, runs counter to
1277               filter-repo’s "avoid mixing old and new history" design. This
1278               support has required improvements to core git as well (e.g. it
1279               depends upon the --reference-excluded-parents option to
1280               fast-export that was added specifically for this usage within
1281               filter-repo). The --partial and --refs options will continue to
1282               be supported since there are people with usecases for them;
1283               however, I am concerned that this inconsistency about mixing
1284               old and new history seems likely to lead to user mistakes. For
1285               now, I just hope that long explanations of caveats in the
1286               documentation of these options suffice to curtail any such
1287               problems.
1288
1289       Comments on reversibility
1290           Some people are interested in reversibility of of a rewrite; e.g.
1291           rewrite history, possibly add some commits, then unrewrite and get
1292           the original history back plus a few new "unrewritten" commits.
1293           Obviously this is impossible if your rewrite involves throwing away
1294           information (e.g. filtering out files or replacing several
1295           different strings with ***REMOVED***), but may be possible with
1296           some rewrites. filter-repo is likely to be a poor fit for this type
1297           of workflow for a few reasons:
1298
1299           ·   most of the limitations inherited from fast-export and
1300               fast-import are of a type that cause reversibility issues
1301
1302           ·   grafts and replace refs, if present, are used in the rewrite
1303               and made permanent
1304
1305           ·   rewriting of commit hashes will probably be reversible, but it
1306               is possible for rewritten abbreviated hashes to not be unique
1307               even if the original abbreviated hashes were.
1308
1309           ·   filter-repo defaults to several forms of unreversible rewriting
1310               that you may need to turn off (e.g. the last two bullet points
1311               above or reencoding commit messages into UTF-8); it’s possible
1312               that additional forms of unreversible rewrites will be added in
1313               the future.
1314
1315           ·   I assume that people use filter-repo for one-shot conversions,
1316               not ongoing data transfers. I explicitly reserve the right to
1317               change any API in filter-repo based on this presumption (and a
1318               comment to this effect is found in multiple places in the code
1319               and examples). You have been warned.
1320

SEE ALSO

1322       git-rebase(1), git-filter-branch(1)
1323

GIT

1325       Part of the git(1) suite
1326

NOTES

1328        1. GitLab’s docs on reducing repository size
1329           https://docs.gitlab.com/ee/user/project/repository/reducing_the_repo_size_using_git.html
1330
1331
1332
1333Git 2.29.0.dirty                  10/19/2020                GIT-FILTER-REPO(1)
Impressum