Commits

Nathan Durnan committed 953c4e3 Merge

Merge with Release 0.2 (new JSON format for timestamp file)

Comments (0)

Files changed (4)

-.hgignore,1305849292.78
-.hgtags,1328835834.16
-README.rst,1328834985.86
-TimeStampMod.png,1305052883.0
-TimestampMod.py,1328835020.92
-TimeStampMod.xmind,1306451621.66
+{
+"Version": "0.2.1",
+"FileData":{
+".hgignore": 1305849292.78,
+".hgtags": 1328835834.16,
+"README.md": 1335288227.96,
+"TimeStampMod.png": 1305052883.0,
+"TimestampMod.py": 1335208918.94,
+"TimeStampMod.xmind": 1306451621.66,
+"TimestampMod_LastRecord": 0
+}
+}
+TimestampMod
+============
+About (version 0.2.1 beta)
+--------------------------
+TimestampMod is an extension for Mercurial DVCS that incorporates automatic saving and restoring of the modification times of files under version control.  This extension is based on an original timestamp extension by Friedrich Kastner-Masilko [face@snoopie.at]() hosted at [https://bitbucket.org/face/timestamp]().
+
+The intent of this extension is to ease the transition from manual file system based version control into the Mercurial DVCS world. Typically, manual file system based version control methods rely on copying and synchronizing files centered around file modification timestamp information. Under any modern VCS, this timestamp information is regarded as unimportant since the VCS manages all of the revision tracking between revision records. This means that the default behavior of most VCS is to set the file modification time to the time that the file was updated by the VCS instead of the original modification time. This is a behavior that can be confusing and lead to mistrust of the VCS by new users who are not yet familiar with VCS concepts. 
+
+Incorporating automatic timestamp saving and restoring into an extension allows Mercurial to add an extra level of comfort to those users who are more familiar with manually managing their version control via traditional file management. 
+
+Requirements
+------------
+This extension was developed for use with the following:
+
+ - [TortoiseHg][thg] 2.0.4 (or later)
+ - [Mercurial][hg] 1.8.3 (or later, included with TortoiseHg)
+
+[thg]: http://tortoisehg.bitbucket.org/
+[hg]: http://mercurial.selenic.com/
+
+Note that only Mercurial is required for the use of this extension.  However, there are some peculiarities in the code specific to use with TortoiseHg.  It may also be worth noting that this extension was developed and tested only on Windows XP SP3, so functionality under other operating systems my not be one-hundred-percent reliable.
+
+- - -
+Installation
+------------
+Begin by downloading the [TimestampMod.py][dl] file and saving it on your computer.  A typical location for the file would be in an "Extensions" under the TortoiseHg program folder (usually "C:\Program Files\TortoiseHg\").  Note that this is not a default folder in the TortoiseHg installation, and must be added manually.
+
+[dl]: https://bitbucket.org/nedmech/timestampmod/downloads/TimestampMod.py
+
+Then edit the appropriate configuration file:
+
+ - The global configuration file - either:
+    1. **mercurial.ini** for TortoiseHg, or 
+    2. **hgrc** for a stand-alone Mercurial installation.
+ - Each individual repository configuration file (**.hg\\hgrc**) where the extension is to be used.
+
+Add an entry into the extensions section of the configuration file similar to the following::
+    
+    [extensions]
+    TimestampMod = /path/to/TimestampMod.py
+
+If using TortoiseHg, some of the hooks will need to be manually configured.  Add entries into the hooks section of the configuration file (**mercurial.ini**) similar to the following::
+    
+    [hooks]
+    post-merge.TimestampMod = python:/path/to/TimestampMod.py:Hook_Post_Merge
+    post-resolve.TimestampMod = python:/path/to/TimestampMod.py:Hook_Post_Resolve
+    post-revert.TimestampMod = python:/path/to/TimestampMod.py:Hook_Post_Revert
+
+Nothing further is required to make the extension work.  It will set up it's own hooks that will trigger during committing and updating actions automatically.
+
+- - -
+Release Notes:
+--------------
+These release notes will not cover every version, just tip of the repository and previous major release points.
+
+### __0.2.1 (beta)__ - _April 24, 2012_
+This is a major beta release.  The format of the timestamp file (.hgtimestamp) has changed with this release!
+
++ __New__ - Features or functionality added with this version:
+  * None
++ __Changed__ - Features or functionality modified with this version:
+  * The timestamp file (.hgtimestamp) format has changed from a simple CSV file to JSON format in order to accommodate filenames with commas or other non-text characters.
++ __Fixed__ - Issues fixed with this version:
+  * Timestamps are not restored for files with a comma in the filename.  This is because the timestamp file (.hgtimestamp) was previously a simple CSV file.  Anything after the first comma on a line was treated as the timestamp value.  
+  _(fixed in TimestampMod v0.2.1)_
++ __Known Issues__ - Outstanding issues with this version that have been identified but not yet addressed:
+  * None
+
+### __0.1.5 (beta)__ - _February 9, 2012_
+This is a stable beta release that addresses several previous issues.  
+It is nearly ready to move from beta to production!
+
++ __New__ - Features or functionality added with this version:
+  * Must manually define post-merge and post-resolve hooks when used with TortoiseHg in order for timestamps to be updated correctly after a merge.
+  * Individual file timestamps can be restored when using the Revert command now.  This requires manually adding a post-revert hook when used with TortoiseHg.
++ __Changed__ - Features or functionality modified with this version:
+  * The timestamp file (.hgtimestamp) is now __sorted__ alphanumerically, treating all text as lowercase.  This allows for consistent ordering of the filenames in the file so that merges can be managed more easily.
++ __Fixed__ - Issues fixed with this version:
+  * Committing Subrepository fails with message "'NoneType' object has no attribute 'always'".  This error was caused by a change in the Mercurial API from 1.9.x to 2.0.x.  
+  _(fixed in TimestampMod v0.1.1)_
+  * Merging typically results in messy manual handling of the timestamp file (.hgtimestamp).  This was caused by inconsistent ordering of the filenames in the timestamp file.  
+  _(fixed in TimestampMod v.01.2)_
+  * Timestamp file (.hgtimestamp) is not always created or added correctly when a new repository is first created.  This can usually be solved by trying to commit a second time.  
+  _(fixed in TimestampMod v0.1.3)_
+  * File timestamps are not updated correctly after a merge.  The timestamp file (.hgtimestamp) has the correct times, and timestamps will be restored after an Update, but not after a merge.  Additional post-merge and post-resolve hooks were required to fix this issue.  
+  _(fixed in TimestampMod v0.1.4)_
+  * 'Transplant --continue' command fails with "TypeError: cannot concatenate 'str' and 'list' objects" error message.  
+  _(fixed in TimestampMod v0.1.5)_
++ __Known Issues__ - Outstanding issues with this version that have been identified but not yet addressed:
+  * None
+
+### __0.1.0 (beta)__ - _August 9, 2011_
+This is an initial beta release.  Undiscovered issues are expected. 
+
+- - -
+!!! Known Issues !!!
+--------------------
+Every attempt is made to keep this extension up-to-date and in working order,but there are occasional problems that arise in the development process.  These issues will be noted here, and possible work-around recommendations provided if at all possible.
+
+* Support has been added for restoring timestamps of individual files or groups  of files when the "Revert" command is used.  This features works well using Mercurial directly (command-line), but does not work at all for most TortoiseHg menu command and dialogs.  As of TortoiseHg 2.2.2, there is an [outstanding issue][thg1347] with how the "Revert" command has been implemented within TortoiseHg that does not allow it to take advantage of the hooks from extensions like this one.  Currently (TortoiseHg 2.2.2), the only command that seems to work from TortoiseHg is the Windows Explorer context menu "TortiseHg -> Revert Files..." item.
+* Because the timestamp record file is modified on *every* commit, there are nearly always merge conflicts with the timestamp file.  Most of the time, these are trivial and can be resolved with a simple "Tool Resolve" option.  Manually resolving the differences is not too difficult either.  Just keep in mind that a larger timestamp number means a newer timestamp.  This issue can affect any commands that involve merging such as graft or import (when dealing with patches).  No further work will be done on this issue.  After spending much time imagining how this could be automated, it was decided that it is best left to the user to determine the most appropriate timestamps to keep when merging the timestamp file.
+
+[thg1347]: https://bitbucket.org/tortoisehg/thg/issue/1347/add-support-for-pre-and-post-hooks
+
+- - -
+Further Reading
+---------------
+If you are not familiar with installing extensions for Mercurial/TortoiseHg, it is *strongly* recommended to review the [UsingExtensions][uex] wiki page from Mercurial. The wiki documents the basic use of extensions, as well as provides links to additional extensions that you may find useful.
+
+[uex]: http://mercurial.selenic.com/wiki/UsingExtensions
+
+Open Source Project Reference
+-----------------------------
+Project statistics for this extension are tracked using the [Ohloh](https://www.ohloh.net) public open-source software directory.  Please reference the following for this extension and its dependencies:
+
+* [Mercurial TimestampMod Extension](https://www.ohloh.net/p/hgTimestampMod)
+* [Mercurial Project](https://www.ohloh.net/p/mercurial)
+* [TortoiseHg Project](https://www.ohloh.net/p/tortoisehg)
+

README.rst

-.. -*-restructuredtext-*- 
-
-============
-TimeStampMod
-============
-About (version 0.1.5 beta)
---------------------------
-TimeStampMod is an extension for Mercurial DVCS that incorporates automatic 
-saving and restoring of the modification times of files under version control.
-This extension is based on an original timestamp extension by Friedrich Kastner-
-Masilko <face@snoopie.at> hosted at https://bitbucket.org/face/timestamp.
-
-The intent of this extension is to ease the transition from manual file system 
-based version control into the Mercurial DVCS world. Typically, manual file 
-system based version control methods rely on copying and synchronizing files 
-centered around file modification timestamp information. Under any modern VCS, 
-this timestamp information is regarded as unimportant since the VCS manages all
-of the revision tracking between revision records. This means that the default 
-behavior of most VCS is to set the file modification time to the time that the
-file was updated by the VCS instead of the original modification time. This is
-a behavior that can be confusing and lead to mistrust of the VCS by new users 
-who are not yet familiar with VCS concepts. 
-
-Incorporating automatic timestamp saving and restoring into an extension allows 
-Mercurial to add an extra level of comfort to those users who are more familiar 
-with manually managing their version control via traditional file management. 
-
-Requirements
-------------
-This extension was developed for use with the following:
- - TortoiseHg_ 2.0.4 (or later)
- - Mercurial_ 1.8.3 (or later, included with TortoiseHg)
-
-.. _TortoiseHg: http://tortoisehg.bitbucket.org/
-.. _Mercurial: http://mercurial.selenic.com/
-
-Note that only Mercurial is required for the use of this extension.  However,
-there are some peculiarities in the code specific to use with TortoiseHg.  It 
-may also be worth noting that this extension was developed and tested only on
-Windows XP SP3, so functionality under other operating systems my not be one-
-hundred-percent reliable.
-
-Installation
-------------
-Begin by downloading the TimestampMod.py_ file and saving it on your computer.  
-A typical location for the file would be in an "Extensions" under the TortoiseHg 
-program folder (usually "C:\Program Files\TortoiseHg\").  Note that this is not 
-a default folder in the TortoiseHg installation, and must be added manually.
-
-Then edit the appropriate configuration file:
-
-.. _TimestampMod.py: https://bitbucket.org/nedmech/timestampmod/downloads/TimestampMod.py
-
- - The global configuration file - either:
- 
-  1. **mercurial.ini** for TortoiseHg, or 
-  2. **hgrc** for a stand-alone Mercurial installation.
-
- - Each individual repository configuration file (**.hg\\hgrc**) where the extension is to be used.
-
-Add an entry into the extensions section of the configuration file similar to 
-the following::
-    
-    [extensions]
-    TimestampMod = /path/to/TimestampMod.py
-
-If using TortoiseHg, some of the hooks will need to be manually configured.  
-Add entries into the hooks section of the configuration file (**mercurial.ini**) 
-similar to the following::
-    
-    [hooks]
-    post-merge.TimestampMod = python:/path/to/TimestampMod.py:Hook_Post_Merge
-    post-resolve.TimestampMod = python:/path/to/TimestampMod.py:Hook_Post_Resolve
-    post-revert.TimestampMod = python:/path/to/TimestampMod.py:Hook_Post_Revert
-
-Nothing further is required to make the extension work.  It will set up it's 
-own hooks that will trigger during committing and updating actions automatically.
-
-For advanced users:
-~~~~~~~~~~~~~~~~~~~
-Optionally, if using the extension from TortoiseHg's installation of Mercurial, 
-the extension can be installed by compiling the TimestampMod.py Python code into
-its TimestampMod.pyo compiled form and inserting it into TortoiseHg's  
-**library.zip file**.  This eliminates the need to specify a path when enabling 
-the extensions, and the extension will show up in TortoiseHg's configuration dialogs.  
-
-The simplest way to compile it for your version is to:
- 1. Install it normally as described above.
- 2. Run a 'hg timestamp_mod --help' command to verify that it is installed correctly.
- 3. Copy the resulting TimestampMod.pyo file into the root of the library.zip file.
-
-This is only recommended if you have experience with manipulating the library.zip
-file for TortoiseHg.  
-
-
-!!! Known Issues !!!
---------------------
-Every attempt is made to keep this extension up-to-date and in working order,
-but there are occasional problems that arise in the development process.  These
-issues will be noted here, and possible work-around recommendations provided if
-at all possible.
-
-* Support has been added for restoring timestamps of individual files or groups 
-  of files when the "Revert" command is used.  This features works well using
-  Mercurial directly (command-line), but does not work at all for most TortoiseHg
-  menu command and dialogs.  As of TortoiseHg 2.2.2, there is an `outstanding issue 
-  <https://bitbucket.org/tortoisehg/thg/issue/1347/add-support-for-pre-and-post-hooks>`_ 
-  with how the "Revert" command has been implemented within TortiseHg that does not 
-  allow it to take advantage of the hooks from extensions like this one.  
-  Currently (TortoiseHg 2.2.2), the only command that seems to work from TortoiseHg 
-  is the Windows Explorer context menu "TortiseHg > Revert Files..." item.
-* Because the timestamp record file is modified on *every* commit, there are
-  nearly always merge conflicts with the timestamp file.  Most of the time, these
-  are trivial and can be resolved with a simple "Tool Resolve" option.  Manually 
-  resolving the differences is not too difficult either.  Just keep in mind that 
-  a larger timestamp number means a newer timestamp.  This issue can affect any 
-  commands that involve merging such as graft or import (when dealing with patches).
-  No further work will be done on this issue.  After spending much time imagining 
-  how this could be automated, it was decided that it is best left to the user to 
-  determine the most appropriate timestamps to keep when merging the timestamp file.
-
-
-Further Reading
----------------
-If you are not familiar with installing extensions for Mercurial/TortoiseHg, it
-is *strongly* recommended to review the UsingExtensions_ wiki page from Mercurial.
-The wiki documents the basic use of extensions, as well as provides links to 
-additional extensions that you may find useful.
-
-.. _UsingExtensions: http://mercurial.selenic.com/wiki/UsingExtensions
-
 #==============================================================================
 # TimestampMod.py - Automatically save and restore the modification times of files
-# Version 0.1.5  --> !!BETA RELEASE!!!
+File_Version = '0.2.1'	# Version number definition
+# --> !!BETA RELEASE!!! <--
 # Copyright 2011-2012 Nathan Durnan <nedmech@gmail.com>
 #
 # Based on timestamp extension by Friedrich Kastner-Masilko <face@snoopie.at>
 #	recorded and restored during commit, update, revert, etc. operations.
 #
 # Development information:
-#	Mercurial Version:	1.8.3 - 2.0.1 (from TortoiseHg package)
+#	Mercurial Version:	1.8.3 - 2.1.1 (from TortoiseHg package)
 #	Python Version:		2.6.6 (from TortoiseHg package)
-#	TortoiseHg Version: 2.0.4 - 2.2.2
+#	TortoiseHg Version: 2.0.4 - 2.3.1
 #==============================================================================
 # begin Extension Help Text:
 '''Automatically store and retrieve modification times of files.'''
 import sys	#requied for debugging/exception info.
 import os	#required for filesystem access methods.
 import time	#required for time functions.
+import json	#use JSON format for storing timestamp data in file.
+import inspect	#required for getting path/name of current code module.
 from mercurial import localrepo	#required for creating a pseudo-pre-commit hook.
 from mercurial import match as matchmod	#required for "match" object in commit wrapper.
 from mercurial import cmdutil	#required for working with "Revert" command methods.
 # Global Objects
 #------------------------------------------------------------------------------
 File_TimestampRecords = '.hgtimestamp'
+LastPlaceholder = 'TimestampMod_LastRecord'
 TimeStamp_dict = dict()
 #_ end of global objects ______________________________________________________
 
 		#end of loop through pattern list
 	#end of check for 'all' flag
 	# Retrieve existing timestamps from the record file.
-	myErr = _read_TimestampRecords(repo, (File_TimestampRecords + '.revert'), TimeStamp_dict)
+	myErr = _read_TimestampJSONRecords(repo, (File_TimestampRecords + '.revert'), TimeStamp_dict)
 	if (not bDryRun):
 		if not myErr:
 			_restore_Timestamps(repo, TimeStamp_dict)
 	else:
 		ui.debug("-----\nmatch argument not specified\n")
 	# end of check for match argument'''
+	# Build initial file lists from the repository contents.
 	myChangedList, myDroppedList = _get_RepoFileList(repo, myMatchList, TimeStamp_dict)
 	# Retrieve existing timestamps from the record file.
-	myErr = _read_TimestampRecords(repo, File_TimestampRecords, TimeStamp_dict)
+	myErr = _read_TimestampJSONRecords(repo, File_TimestampRecords, TimeStamp_dict)
 	
 	# Check for command optional argument
 	if kwargs['save']:
-		_save_Timestamps(repo, File_TimestampRecords, myChangedList, TimeStamp_dict)
+		_save_TimestampsJSON(repo, File_TimestampRecords, myChangedList, myDroppedList, TimeStamp_dict)
 	elif not myErr:	# Only evaluate Restore or Display if file was read.
 		if kwargs['restore']:
 			_restore_Timestamps(repo, TimeStamp_dict)
 			[('s', 'save', None, ('save modification times')),
 			('r', 'restore', None, ('restore modification times'))
 			], 
-			('hg timestamp_mod [-s | -r]')
+			('hg timestamp_mod [-s | -r] \n' +
+				'\n' + inspect.getfile(inspect.currentframe()) +
+				'\n  ' + '(Version ' + File_Version + ')')
 		) #end of timestamp_mod
 }
 #_ end of cmdtable ____________________________________________________________
 	# Rebuild global dictionary collection
 	OUT_TimeStamp_dict.clear() # Be sure to start with a clean collection.
 	for myFile in myFiles:
-		myFileName = str(myFile).strip()
-		OUT_TimeStamp_dict[myFileName] = -1	# initialize dictionary entry
-		repo.ui.debug('Tracking:  ', myFileName, '\n')
+		if myFile not in myDropped:
+			#Only add non-dropped files to the list, even if they are part of the match list.
+			myFileName = str(myFile).strip()
+			OUT_TimeStamp_dict[myFileName] = -1	# initialize dictionary entry
+			repo.ui.debug('Tracking:  ', myFileName, '\n')
+		# end of check for droppped files.
 	# end of loop through repo files.
 	myReturnList = myChanged, myDropped
 	return myReturnList
 			# Read the data from the line. (CSV format: [FileName],[ModificationTime])
 			try:
 				myFileName, myModTime = myLine.strip().split(',')
+				#Only import data for entries already in local dictionary.
 				if myFileName in INOUT_TimeStamp_dict:
-					INOUT_TimeStamp_dict[myFileName] = float(myModTime)
+					#Make sure incoming data is properly formatted.
+					INOUT_TimeStamp_dict[str(myFileName)] = float(myModTime)
 					repo.ui.debug('UTC: ', myModTime, '\t: ', myFileName, '\n')
 				# end of check file exists in dictionary.
 			except:
 		myTimeStampRecordsFile.close()
 		return False 	# no return flag as successful.
 	except:
-		repo.ui.debug('*** Error accessing ', IN_TimestampFileName, ' file! ***\n')
+		repo.ui.warn('*** Error accessing ', IN_TimestampFileName, ' file! ***\n')
 		repo.ui.debug('*** Exception: ', str(sys.exc_info()), '  ***\n')
-		return False	# set return flag on error.
+		return True	# set return flag on error.
 	# end of accessing record file.
 #_ end of _read_TimestampRecords ______________________________________________
 
 
 
 #==============================================================================
-# _save_Timestamps Function Definition
+# _read_TimestampJSONRecords Function Definition
+#------------------------------------------------------------------------------
+# Summary: Read in the data from the Timestamp JSON Record File and assign the 
+#	timestamps to their corresponding entries in the global file dictionary
+#	collection object.
+# NOTES:
+# * This method will fall back to use the previous CSV file read method if it
+#	can not read the file as JSON data.
+#==============================================================================
+def _read_TimestampJSONRecords(repo, IN_TimestampFileName, INOUT_TimeStamp_dict):
+	'''Read data from Timestamp JSON Record File.'''
+	myReturn = True # start with an error value
+	myTimeStampJSONFile = ''
+	try:
+		myTimeStampJSONFile = open(repo.wjoin(IN_TimestampFileName), 'r')
+		repo.ui.debug('______\nRetrieving timestamps from JSON record file:\n------\n')
+		try:
+			myData = json.load(myTimeStampJSONFile)
+			# Check for version and object data:
+			if ('FileData' in myData):
+				if (LastPlaceholder in myData['FileData']):
+					#Don't import placeholder into local dictionary.
+					del myData['FileData'][LastPlaceholder]
+				for s_fileName, f_fileModTime in myData['FileData'].items():
+					#Only import data for entries already in local dictionary.
+					if s_fileName in INOUT_TimeStamp_dict:
+						#Make sure incoming data is properly formatted.
+						INOUT_TimeStamp_dict[str(s_fileName)] = float(f_fileModTime)
+						repo.ui.debug('UTC: ', str(f_fileModTime), '\t: ', str(s_fileName), '\n')
+					# end of check file exists in dictionary.
+				myReturn = False 	# no return flag as successful.
+			else: # JSON data is not present
+				repo.ui.debug(IN_TimestampFileName, ' does not have JSON file data.\n')
+				myReturn = True	# error - could not find JSON data
+			# end of check for file data.
+		except: # report errors
+			repo.ui.debug('Error loading JSON file\n')
+			repo.ui.debug('*** Exception: ', str(sys.exc_info()), '  ***\n')
+		finally: #cleanup clause
+			myTimeStampJSONFile.close()
+			if myReturn: #error return detected - try older version.
+				# Try opening as a pre-v0.2.0 CSV file insted:
+				repo.ui.warn('Opening ', IN_TimestampFileName, ' as v0.1.5 file\n')
+				myReturn = _read_TimestampRecords(repo, IN_TimestampFileName, INOUT_TimeStamp_dict)
+		# end of reading record file.
+		return myReturn
+	except: #report errors
+		repo.ui.warn('*** Error accessing ', IN_TimestampFileName, ' file! ***\n')
+		repo.ui.debug('*** Exception: ', str(sys.exc_info()), '  ***\n')
+		return True	# set return flag on error.
+	# end of accessing record file.
+#_ end of _read_TimestampJSONRecords ______________________________________________
+
+
+#==============================================================================
+# _save_TimestampsJSON Function Definition
 #------------------------------------------------------------------------------
 # Summary: Save File Modified Timestamp data for files in the global dictionary
-#	collection to a record file in the repository root directory.  New values
-#	for timestamps will be retrieved for files that have changed or are missing
-#	timestamp records in the first place.
-# NOTE:
-#	Many thanks to BitBucket user lboehler (https://bitbucket.org/lboehler) for
+#	collection to a JSON-formatted record file in the repository root directory.  
+#	New values for timestamps will be retrieved for files that have changed or 
+#	are missing timestamp records in the first place.
+# NOTES:
+# * Many thanks to BitBucket user lboehler (https://bitbucket.org/lboehler) for
 #	suggesting using the SORTED list to build the timestamp file.  This really 
 #	should have been obvious, but I completely overlooked it!  
+# * The JSON file will be built manually instead of using the built-in JSON 
+#	methods.  This is because the dict object in Python can no be sorted well
+#	enough to generate a repeatable JSON file with the data in the same order
+#	all the time.  Maybe when Mercurial starts using Python 2.7+, the newer 
+#	OrderedDict object type may be able to be used here.
 #==============================================================================
-def _save_Timestamps(repo, IN_TimestampFileName, IN_ChangedList, INOUT_TimeStamp_dict):
-	'''Save File Modification Timestamps to record file.'''
-	repo.ui.note('______\nSaving timestamps...\n------\n')
-	myTimeStampRecordsFile = open(repo.wjoin(IN_TimestampFileName), 'w')
+def _save_TimestampsJSON(repo, IN_TimestampFileName, IN_ChangedList, IN_DroppedList, INOUT_TimeStamp_dict):
+	'''Save File Modification Timestamps to JSON record file.'''
+	repo.ui.note('______\nSaving timestamps to JSON file...\n------\n')
 	# Remove the record file from the list.
 	#	It causes confusion and difficulty during merge.
 	if IN_TimestampFileName in INOUT_TimeStamp_dict:
 		del INOUT_TimeStamp_dict[IN_TimestampFileName]
 	# end of check for record file.
+	myTimeStampJSONFile = open(repo.wjoin(IN_TimestampFileName), 'w')
+	# Record version data for the file.
+	myTimeStampJSONFile.write('{\n"Version": "' + File_Version + '",\n"FileData":{\n')
+	# Make sure to use a sorted dictionary for the file data:
 	for s_fileName in sorted(INOUT_TimeStamp_dict.keys(), key=str.lower):
 		f_fileModTime = INOUT_TimeStamp_dict[s_fileName]
 		if (s_fileName in IN_ChangedList) or (f_fileModTime<=0):
 			f_fileModTime = INOUT_TimeStamp_dict[s_fileName] = _get_fileModTime(repo, s_fileName)
 		# end of update timestamps for changed items or missing timestamps.
-		myTimeStampRecordsFile.write("%s,%s\n" % (s_fileName, f_fileModTime))
+		if (s_fileName in IN_DroppedList) or (f_fileModTime<=0):
+			repo.ui.debug('...removing record of dropped file, or file with missing timestamp (', s_fileName, ')\n')
+			del INOUT_TimeStamp_dict[s_fileName]
+		else:	#timestamp is valid
+			myTimeStampJSONFile.write('"%s": %s,\n' % (s_fileName, f_fileModTime))
+		# end of check for non-existing files or timestamps.
 	# end of loop through dictionary items.
+	# Add one last record to wrap up the JSON formatting.
+	myTimeStampJSONFile.write('"' + LastPlaceholder + '": 0\n}\n}')
 	# Make sure to close the file!
-	myTimeStampRecordsFile.close()
-#_ end of _save_Timestamps ____________________________________________________
-
+	myTimeStampJSONFile.close()
+#_ end of _save_TimestampsJSON ____________________________________________________
 
 
 #==============================================================================