@@ -2947,6 +2947,9 @@ class RepoFilter(object):
2947
2947
# now-missing commit hash, since there was nothing to map it to.
2948
2948
self ._commits_referenced_but_removed = set ()
2949
2949
2950
+ # Other vars related to metadata tracking
2951
+ self ._already_ran = False
2952
+
2950
2953
# Progress handling (number of commits parsed, etc.)
2951
2954
self ._progress_writer = ProgressWriter ()
2952
2955
self ._num_commits = 0
@@ -2967,31 +2970,30 @@ class RepoFilter(object):
2967
2970
2968
2971
# Compile some regexes and cache those
2969
2972
self ._hash_re = re .compile (br'(\b[0-9a-f]{7,40}\b)' )
2970
- self ._full_hash_re = re .compile (br'(\b[0-9a-f]{40}\b)' )
2971
2973
2972
2974
def _handle_arg_callbacks (self ):
2973
- def make_callback (argname , str ):
2975
+ def make_callback (argname , bdy ):
2974
2976
callback_globals = {g : globals ()[g ] for g in public_globals }
2975
2977
callback_locals = {}
2976
2978
exec ('def callback({}, _do_not_use_this_var = None):\n ' .format (argname )+
2977
- ' ' + '\n ' .join (str .splitlines ()), callback_globals , callback_locals )
2979
+ ' ' + '\n ' .join (bdy .splitlines ()), callback_globals , callback_locals )
2978
2980
return callback_locals ['callback' ]
2979
- def handle (type ):
2980
- callback_field = '_{}_callback' .format (type )
2981
- code_string = getattr (self ._args , type + '_callback' )
2981
+ def handle (which ):
2982
+ callback_field = '_{}_callback' .format (which )
2983
+ code_string = getattr (self ._args , which + '_callback' )
2982
2984
if code_string :
2983
2985
if os .path .exists (code_string ):
2984
2986
with open (code_string , 'r' , encoding = 'utf-8' ) as f :
2985
2987
code_string = f .read ()
2986
2988
if getattr (self , callback_field ):
2987
2989
raise SystemExit (_ ("Error: Cannot pass a %s_callback to RepoFilter "
2988
2990
"AND pass --%s-callback"
2989
- % (type , type )))
2991
+ % (which , which )))
2990
2992
if 'return ' not in code_string and \
2991
- type not in ('blob' , 'commit' , 'tag' , 'reset' ):
2993
+ which not in ('blob' , 'commit' , 'tag' , 'reset' ):
2992
2994
raise SystemExit (_ ("Error: --%s-callback should have a return statement" )
2993
- % type )
2994
- setattr (self , callback_field , make_callback (type , code_string ))
2995
+ % which )
2996
+ setattr (self , callback_field , make_callback (which , code_string ))
2995
2997
handle ('filename' )
2996
2998
handle ('message' )
2997
2999
handle ('name' )
@@ -3023,8 +3025,8 @@ class RepoFilter(object):
3023
3025
# Determine if this is second or later run of filter-repo
3024
3026
tmp_dir = self .results_tmp_dir (create_if_missing = False )
3025
3027
ran_path = os .path .join (tmp_dir , b'already_ran' )
3026
- already_ran = os .path .isfile (ran_path )
3027
- if already_ran :
3028
+ self . _already_ran = os .path .isfile (ran_path )
3029
+ if self . _already_ran :
3028
3030
current_time = time .time ()
3029
3031
file_mod_time = os .path .getmtime (ran_path )
3030
3032
file_age = current_time - file_mod_time
@@ -3036,17 +3038,17 @@ class RepoFilter(object):
3036
3038
3037
3039
if response .lower () != 'y' :
3038
3040
os .remove (ran_path )
3039
- already_ran = False
3041
+ self . _already_ran = False
3040
3042
3041
3043
# Default for --replace-refs
3042
3044
if not self ._args .replace_refs :
3043
3045
self ._args .replace_refs = 'delete-no-add'
3044
3046
if self ._args .replace_refs == 'old-default' :
3045
- self ._args .replace_refs = ('update-or-add' if already_ran
3047
+ self ._args .replace_refs = ('update-or-add' if self . _already_ran
3046
3048
else 'update-and-add' )
3047
3049
3048
3050
# Do sanity checks from the correct directory
3049
- if not self ._args .force and not already_ran :
3051
+ if not self ._args .force and not self . _already_ran :
3050
3052
cwd = os .getcwd ()
3051
3053
os .chdir (target_working_dir )
3052
3054
RepoFilter .sanity_check (self ._orig_refs , is_bare , self ._config_settings )
@@ -3278,13 +3280,6 @@ class RepoFilter(object):
3278
3280
assert new_hash is not None
3279
3281
return new_hash [0 :orig_len ]
3280
3282
3281
- def _translate_full_commit_hash (self , matchobj ):
3282
- old_hash = matchobj .group (1 )
3283
- new_hash = self ._get_rename (old_hash )
3284
- if new_hash is None :
3285
- return old_hash
3286
- return new_hash
3287
-
3288
3283
def _maybe_trim_extra_parents (self , orig_parents , parents ):
3289
3284
'''Due to pruning of empty commits, some parents could be non-existent
3290
3285
(None) or otherwise redundant. Remove the non-existent parents, and
@@ -3754,6 +3749,10 @@ class RepoFilter(object):
3754
3749
orig_file_changes = set (commit .file_changes )
3755
3750
self ._filter_files (commit )
3756
3751
3752
+ # Call the user-defined callback, if any
3753
+ if self ._commit_callback :
3754
+ self ._commit_callback (commit , self .callback_metadata (aux_info ))
3755
+
3757
3756
# Find out which files were modified by the callbacks. Such paths could
3758
3757
# lead to subsequent commits being empty (e.g. if removing a line containing
3759
3758
# a password from every version of a file that had the password, and some
@@ -3765,10 +3764,6 @@ class RepoFilter(object):
3765
3764
differences = orig_file_changes .symmetric_difference (final_file_changes )
3766
3765
self ._files_tweaked .update (x .filename for x in differences )
3767
3766
3768
- # Call the user-defined callback, if any
3769
- if self ._commit_callback :
3770
- self ._commit_callback (commit , self .callback_metadata (aux_info ))
3771
-
3772
3767
# Now print the resulting commit, or if prunable skip it
3773
3768
if not commit .dumped :
3774
3769
if not self ._prunable (commit , new_1st_parent ,
@@ -3781,8 +3776,13 @@ class RepoFilter(object):
3781
3776
if self ._args .state_branch :
3782
3777
alias = Alias (commit .old_id or commit .id , rewrite_to or deleted_hash )
3783
3778
self ._insert_into_stream (alias )
3784
- reset = Reset (commit .branch , rewrite_to or deleted_hash )
3785
- self ._insert_into_stream (reset )
3779
+ if commit .branch .startswith (b'refs/' ) or commit .branch == b'HEAD' :
3780
+ # The special check above is because when direct revisions are passed
3781
+ # along to fast-export (such as with stashes), there is a chance the
3782
+ # revision is rewritten to nothing. In such cases, we don't want to
3783
+ # point an invalid ref that just names a revision to some other point.
3784
+ reset = Reset (commit .branch , rewrite_to or deleted_hash )
3785
+ self ._insert_into_stream (reset )
3786
3786
self ._commit_renames [commit .original_id ] = None
3787
3787
3788
3788
# Show progress
@@ -3933,21 +3933,26 @@ class RepoFilter(object):
3933
3933
git_dir = GitUtils .determine_git_dir (repo_working_dir )
3934
3934
stash = os .path .join (git_dir , b'logs' , b'refs' , b'stash' )
3935
3935
if os .path .exists (stash ):
3936
+ self ._stash = []
3936
3937
with open (stash , 'br' ) as f :
3937
- self . _stash = f . read ()
3938
- out = subproc . check_output ( 'git rev-list -g refs/stash' .split (),
3939
- cwd = repo_working_dir )
3940
- self ._args .refs .extend (decode ( out . strip ()). split () )
3938
+ for line in f :
3939
+ ( oldhash , newhash , rest ) = line .split (None , 2 )
3940
+ self . _stash . append (( newhash , rest ) )
3941
+ self ._args .refs .extend ([ x [ 0 ] for x in self . _stash ] )
3941
3942
3942
3943
def _write_stash (self ):
3944
+ last = deleted_hash
3943
3945
if self ._stash :
3944
3946
target_working_dir = self ._args .target or b'.'
3945
3947
git_dir = GitUtils .determine_git_dir (target_working_dir )
3946
3948
stash = os .path .join (git_dir , b'logs' , b'refs' , b'stash' )
3947
3949
with open (stash , 'bw' ) as f :
3948
- self ._stash = self ._full_hash_re .sub (self ._translate_full_commit_hash ,
3949
- self ._stash )
3950
- f .write (self ._stash )
3950
+ for (hash , rest ) in self ._stash :
3951
+ new_hash = self ._get_rename (hash )
3952
+ if new_hash is None :
3953
+ continue
3954
+ f .write (b' ' .join ([last , new_hash , rest ]) + b'\n ' )
3955
+ last = new_hash
3951
3956
print (_ ("Rewrote the stash." ))
3952
3957
3953
3958
def _setup_input (self , use_done_feature ):
@@ -4083,6 +4088,10 @@ class RepoFilter(object):
4083
4088
# Remove unused refs
4084
4089
exported_refs , imported_refs = self .get_exported_and_imported_refs ()
4085
4090
refs_to_nuke = exported_refs - imported_refs
4091
+ # Because revisions can be passed to fast-export which handles them as
4092
+ # though they were refs, we might have bad "refs" to nuke; strip them out.
4093
+ refs_to_nuke = [x for x in refs_to_nuke
4094
+ if x .startswith (b'refs/' ) or x == b'HEAD' ]
4086
4095
if self ._args .partial :
4087
4096
refs_to_nuke = set ()
4088
4097
if refs_to_nuke and self ._args .debug :
@@ -4134,13 +4143,11 @@ class RepoFilter(object):
4134
4143
return new_hash
4135
4144
4136
4145
def _compute_metadata (self , metadata_dir , orig_refs ):
4137
- already_ran = os .path .isfile (os .path .join (metadata_dir , b'already_ran' ))
4138
-
4139
4146
#
4140
4147
# First, handle commit_renames
4141
4148
#
4142
4149
old_commit_renames = dict ()
4143
- if not already_ran :
4150
+ if not self . _already_ran :
4144
4151
commit_renames = {old : new
4145
4152
for old , new in self ._commit_renames .items ()
4146
4153
}
@@ -4169,7 +4176,7 @@ class RepoFilter(object):
4169
4176
exported_refs , imported_refs = self .get_exported_and_imported_refs ()
4170
4177
4171
4178
old_commit_unrenames = dict ()
4172
- if not already_ran :
4179
+ if not self . _already_ran :
4173
4180
old_ref_map = dict ((refname , (old_hash , deleted_hash ))
4174
4181
for refname , old_hash in orig_refs .items ()
4175
4182
if refname in exported_refs )
@@ -4236,7 +4243,7 @@ class RepoFilter(object):
4236
4243
#
4237
4244
4238
4245
old_first_changes = dict ()
4239
- if already_ran :
4246
+ if self . _already_ran :
4240
4247
# Read first_changes into old_first_changes
4241
4248
with open (os .path .join (metadata_dir , b'first-changed-commits' ), 'br' ) as f :
4242
4249
for line in f :
0 commit comments