@ -1673,10 +1673,6 @@ EXAMPLES
"useful in determining what to filter in a subsequent run. "
"useful in determining what to filter in a subsequent run. "
"Will not modify your repo."))
"Will not modify your repo."))
refs = parser.add_argument_group(title=_("Git References"))
refs.add_argument('--refs', nargs='*', default=['--all'],
help=argparse.SUPPRESS)
path = parser.add_argument_group(title=_("Filtering based on paths "
path = parser.add_argument_group(title=_("Filtering based on paths "
"(see also --filename-callback)"))
"(see also --filename-callback)"))
path.add_argument('--invert-paths', action='store_false', dest='inclusive',
path.add_argument('--invert-paths', action='store_false', dest='inclusive',
@ -1846,9 +1842,10 @@ EXAMPLES
"CALLBACKS section below."))
"CALLBACKS section below."))
desc = _(
desc = _(
"Specifying alternate source or target locations will disable some \n"
"Specifying alternate source or target locations implies --partial,\n"
"auxiliary steps such as disconnecting the origin remote, and avoiding\n"
"except that the normal default for --replace-refs is used. However,\n"
"mixing new and old history.")
"unlike normal uses of --partial, this doesn't risk mixing old and new\n"
"history since the old and new histories are in different repositories.")
location = parser.add_argument_group(title=_("Location to filter from/to"),
location = parser.add_argument_group(title=_("Location to filter from/to"),
description=desc)
description=desc)
location.add_argument('--source', type=os.fsencode,
location.add_argument('--source', type=os.fsencode,
@ -1862,6 +1859,29 @@ EXAMPLES
misc.add_argument('--force', '-f', action='store_true',
misc.add_argument('--force', '-f', action='store_true',
help=_("Rewrite history even if the current repo does not look "
help=_("Rewrite history even if the current repo does not look "
"like a fresh clone."))
"like a fresh clone."))
misc.add_argument('--partial', action='store_true',
help=_("Do a partial history rewrite, resulting in the mixture of "
"old and new history. This implies a default of "
"update-no-add for --replace-refs, disables rewriting "
"refs/remotes/origin/* to refs/heads/*, disables removing "
"of the 'origin' remote, disables removing unexported refs, "
"disables expiring the reflog, and disables the automatic "
"post-filter gc. Also, this modifies --tag-rename and "
"--refname-callback options such that instead of replacing "
"old refs with new refnames, it will instead create new "
"refs and keep the old ones around. Use with caution."))
# WARNING: --refs presents a problem with become-degenerate pruning:
# * Excluding a commit also excludes its ancestors so when some other
# commit has an excluded ancestor as a parent we have no way of
# knowing what it is an ancestor of without doing a special
# full-graph walk.
misc.add_argument('--refs', nargs='+',
help=_("Limit history rewriting to the specified refs. Implies "
"--partial. In addition to the normal caveats of --partial "
"(mixing old and new history, no automatic remapping of "
"refs/remotes/origin/* to refs/heads/*, etc.), this also may "
"cause problems for pruning of degenerate empty merge "
"commits when negative revisions are specified."))
misc.add_argument('--dry-run', action='store_true',
misc.add_argument('--dry-run', action='store_true',
help=_("Do not change the repository. Run `git fast-export` and "
help=_("Do not change the repository. Run `git fast-export` and "
@ -2065,6 +2085,12 @@ EXAMPLES
args.strip_blobs_with_ids = set(f.read().split())
args.strip_blobs_with_ids = set(f.read().split())
else:
else:
args.strip_blobs_with_ids = set()
args.strip_blobs_with_ids = set()
if (args.partial or args.refs) and not args.replace_refs:
args.replace_refs = 'update-no-add'
if args.refs or args.source or args.target:
args.partial = True
if not args.refs:
args.refs = ['--all']
return args
return args
class RepoAnalyze(object):
class RepoAnalyze(object):
@ -3475,8 +3501,6 @@ class RepoFilter(object):
.format(decode(self._fe_filt)))
.format(decode(self._fe_filt)))
def _migrate_origin_to_heads(self):
def _migrate_origin_to_heads(self):
if self._args.dry_run or self._args.source or self._args.target:
return
refs_to_migrate = set(x for x in self._orig_refs
refs_to_migrate = set(x for x in self._orig_refs
if x.startswith(b'refs/remotes/origin/'))
if x.startswith(b'refs/remotes/origin/'))
if not refs_to_migrate:
if not refs_to_migrate:
@ -3532,7 +3556,7 @@ class RepoFilter(object):
# Remove unused refs
# Remove unused refs
exported_refs, imported_refs = self.get_exported_and_imported_refs()
exported_refs, imported_refs = self.get_exported_and_imported_refs()
refs_to_nuke = exported_refs - imported_refs
refs_to_nuke = exported_refs - imported_refs
if self._args.source or self._args.target :
if self._args.partial :
refs_to_nuke = set()
refs_to_nuke = set()
if refs_to_nuke and self._args.debug:
if refs_to_nuke and self._args.debug:
print("[DEBUG] Deleting the following refs:\n "+
print("[DEBUG] Deleting the following refs:\n "+
@ -3690,7 +3714,8 @@ class RepoFilter(object):
start = time.time()
start = time.time()
if not self._input and not self._output:
if not self._input and not self._output:
self._run_sanity_checks()
self._run_sanity_checks()
self._migrate_origin_to_heads()
if not self._args.dry_run and not self._args.partial:
self._migrate_origin_to_heads()
self._setup_input(use_done_feature = True)
self._setup_input(use_done_feature = True)
self._setup_output()
self._setup_output()
assert self._sanity_checks_handled
assert self._sanity_checks_handled
@ -3725,7 +3750,7 @@ class RepoFilter(object):
self._save_marks_files()
self._save_marks_files()
# Notify user how long it took, before doing a gc and such
# Notify user how long it took, before doing a gc and such
repack = (not self._args.source and not self._args.target )
repack = (not self._args.partial )
msg = "New history written in {:.2f} seconds..."
msg = "New history written in {:.2f} seconds..."
if repack:
if repack:
msg = "New history written in {:.2f} seconds; now repacking/cleaning..."
msg = "New history written in {:.2f} seconds; now repacking/cleaning..."
@ -3749,7 +3774,7 @@ class RepoFilter(object):
# Write out data about run
# Write out data about run
self._record_metadata(self.results_tmp_dir(), self._orig_refs)
self._record_metadata(self.results_tmp_dir(), self._orig_refs)
# Nuke the reflogs and repack
# If repack, then nuke the reflogs and repack. If reset, do a reset --hard
reset = not GitUtils.is_repository_bare(target_working_dir)
reset = not GitUtils.is_repository_bare(target_working_dir)
RepoFilter.cleanup(target_working_dir, repack, reset,
RepoFilter.cleanup(target_working_dir, repack, reset,
run_quietly=self._args.quiet,
run_quietly=self._args.quiet,