From bd2c79fbfef894d788c256d45cef05d99442407a Mon Sep 17 00:00:00 2001 From: Ian Campbell Date: Thu, 21 Sep 2017 08:49:31 +0100 Subject: [PATCH] filter-branch: stash away ref map in a branch With "--state-branch=" option, the mapping from old object names and filtered ones in ./map/ directory is stashed away in the object database, and the one from the previous run is read to populate the ./map/ directory, allowing for incremental updates of large trees. Signed-off-by: Ian Campbell Signed-off-by: Junio C Hamano --- Documentation/git-filter-branch.txt | 8 ++++- git-filter-branch.sh | 49 ++++++++++++++++++++++++++++- 2 files changed, 55 insertions(+), 2 deletions(-) diff --git a/Documentation/git-filter-branch.txt b/Documentation/git-filter-branch.txt index 9e5169aa64..bebdcdec5a 100644 --- a/Documentation/git-filter-branch.txt +++ b/Documentation/git-filter-branch.txt @@ -14,7 +14,7 @@ SYNOPSIS [--commit-filter ] [--tag-name-filter ] [--subdirectory-filter ] [--prune-empty] [--original ] [-d ] [-f | --force] - [--] [...] + [--state-branch ] [--] [...] DESCRIPTION ----------- @@ -198,6 +198,12 @@ to other tags will be rewritten to point to the underlying commit. directory or when there are already refs starting with 'refs/original/', unless forced. +--state-branch :: + This option will cause the mapping from old to new objects to + be loaded from named branch upon startup and saved as a new + commit to that branch upon exit, enabling incremental of large + trees. If '' does not exist it will be created. + ...:: Arguments for 'git rev-list'. All positive refs included by these options are rewritten. You may also specify options diff --git a/git-filter-branch.sh b/git-filter-branch.sh index 9edb942064..956869b8e3 100755 --- a/git-filter-branch.sh +++ b/git-filter-branch.sh @@ -86,7 +86,7 @@ USAGE="[--setup ] [--env-filter ] [--parent-filter ] [--msg-filter ] [--commit-filter ] [--tag-name-filter ] [--subdirectory-filter ] [--original ] - [-d ] [-f | --force] + [-d ] [-f | --force] [--state-branch ] [--] [...]" OPTIONS_SPEC= @@ -106,6 +106,7 @@ filter_msg=cat filter_commit= filter_tag_name= filter_subdir= +state_branch= orig_namespace=refs/original/ force= prune_empty= @@ -181,6 +182,9 @@ do --original) orig_namespace=$(expr "$OPTARG/" : '\(.*[^/]\)/*$')/ ;; + --state-branch) + state_branch="$OPTARG" + ;; *) usage ;; @@ -259,6 +263,26 @@ export GIT_INDEX_FILE # map old->new commit ids for rewriting parents mkdir ../map || die "Could not create map/ directory" +if test -n "$state_branch" +then + state_commit=$(git rev-parse --no-flags --revs-only "$state_branch") + if test -n "$state_commit" + then + echo "Populating map from $state_branch ($state_commit)" 1>&2 + perl -e'open(MAP, "-|", "git show $ARGV[0]:filter.map") or die; + while () { + m/(.*):(.*)/ or die; + open F, ">../map/$1" or die; + print F "$2" or die; + close(F) or die; + } + close(MAP) or die;' "$state_commit" \ + || die "Unable to load state from $state_branch:filter.map" + else + echo "Branch $state_branch does not exist. Will create" 1>&2 + fi +fi + # we need "--" only if there are no path arguments in $@ nonrevs=$(git rev-parse --no-revs "$@") || exit if test -z "$nonrevs" @@ -590,6 +614,29 @@ test -z "$ORIG_GIT_COMMITTER_DATE" || { export GIT_COMMITTER_DATE } +if test -n "$state_branch" +then + echo "Saving rewrite state to $state_branch" 1>&2 + state_blob=$( + perl -e'opendir D, "../map" or die; + open H, "|-", "git hash-object -w --stdin" or die; + foreach (sort readdir(D)) { + next if m/^\.\.?$/; + open F, "<../map/$_" or die; + chomp($f = ); + print H "$_:$f\n" or die; + } + close(H) or die;' || die "Unable to save state") + state_tree=$(/bin/echo -e "100644 blob $state_blob\tfilter.map" | git mktree) + if test -n "$state_commit" + then + state_commit=$(/bin/echo "Sync" | git commit-tree "$state_tree" -p "$state_commit") + else + state_commit=$(/bin/echo "Sync" | git commit-tree "$state_tree" ) + fi + git update-ref "$state_branch" "$state_commit" +fi + cd "$orig_dir" rm -rf "$tempdir"