예제 #1
0
		// Get status for a single file (non recursive).
		// Will return valid status even if the file has nothing to show (has no changes).
		// If error happened, invalid status data will be returned (check statusData.IsValid).
		public static SVNAsyncOperation<SVNStatusData> GetStatusAsync(string path, bool offline, bool fetchLockDetails = true, int timeout = COMMAND_TIMEOUT)
		{
			// If default timeout, give some more time for online operations, just in case.
			if (timeout == COMMAND_TIMEOUT && !offline) {
				timeout *= 2;
			}

			var options = new SVNStatusDataOptions() {
				Depth = SVNStatusDataOptions.SearchDepth.Empty,
				Timeout = timeout,
				RaiseError = false,
				Offline = offline,
				FetchLockOwner = fetchLockDetails,  // If offline, this is ignored.
			};

			return SVNAsyncOperation<SVNStatusData>.Start(op => {

				var statusData = GetStatuses(path, options).FirstOrDefault();

				// If no path was found, error happened.
				if (!statusData.IsValid) {
					// Fallback to unversioned as we don't touch them.
					statusData.Status = VCFileStatus.Unversioned;
				}

				return statusData;
			});
		}
예제 #2
0
        // Executed in a worker thread.
        protected override GuidStatusDatasBind[] GatherDataInThread()
        {
            var statusOptions = new SVNStatusDataOptions()
            {
                Depth          = SVNStatusDataOptions.SearchDepth.Infinity,
                RaiseError     = false,
                Timeout        = WiseSVNIntegration.ONLINE_COMMAND_TIMEOUT * 2,
                Offline        = !SVNPreferencesManager.Instance.DownloadRepositoryChanges && !SVNPreferencesManager.Instance.ProjectPrefs.EnableAutoLocking,
                FetchLockOwner = true,
            };

            // Will get statuses of all added / modified / deleted / conflicted / unversioned files. Only normal files won't be listed.
            var statuses = WiseSVNIntegration.GetStatuses("Assets", statusOptions)
#if UNITY_2018_4_OR_NEWER
                           .Concat(WiseSVNIntegration.GetStatuses("Packages", statusOptions))
#endif
                           .Where(s => s.Status != VCFileStatus.Missing)
                           .ToList();

            for (int i = 0, count = statuses.Count; i < count; ++i)
            {
                var statusData = statuses[i];

                // Statuses for entries under unversioned directories are not returned. Add them manually.
                if (statusData.Status == VCFileStatus.Unversioned && Directory.Exists(statusData.Path))
                {
                    try {
                        var paths = Directory.EnumerateFileSystemEntries(statusData.Path, "*", SearchOption.AllDirectories)
                                    .Where(path => !WiseSVNIntegration.IsHiddenPath(path))
                        ;

                        statuses.AddRange(paths
                                          .Select(path => path.Replace(WiseSVNIntegration.ProjectRoot, ""))
                                          .Select(path => new SVNStatusData()
                        {
                            Status = VCFileStatus.Unversioned, Path = path, LockDetails = LockDetails.Empty
                        })
                                          );
                    } catch (Exception) {
                        // Files must have changed while scanning. Nothing we can do.
                    }
                }
            }

            // HACK: the base class works with the DataType for pending data. Guid won't be used.
            return(statuses
                   .Where(s => statuses.Count < SanityStatusesLimit ||                  // Include everything when below the limit
                          s.Status == VCFileStatus.Added ||
                          s.Status == VCFileStatus.Modified ||
                          s.Status == VCFileStatus.Conflicted
                          )
                   .Select(s => new GuidStatusDatasBind()
            {
                MergedStatusData = s
            })
                   .ToArray());
        }
예제 #3
0
		// Get statuses of files based on the options you provide.
		// NOTE: data is returned ONLY for folders / files that has something to show (has changes, locks or remote changes).
		//		 If used with non-recursive option it will return single data with normal status (if non).
		// NOTE2: this is a synchronous operation.
		//		 If you use it in online mode it might freeze your code for a long time.
		//		 To avoid this, use the Async version!
		public static IEnumerable<SVNStatusData> GetStatuses(string path, SVNStatusDataOptions options)
		{
			// File can be missing, if it was deleted by svn.
			//if (!File.Exists(path) && !Directory.Exists(path)) {
			//	if (!Silent) {
			//		EditorUtility.DisplayDialog("SVN Error", "SVN error happened while processing the assets. Check the logs.", "I will!");
			//	}
			//	throw new IOException($"Trying to get status for file {path} that does not exist!");
			//}
			var depth = options.Depth == SVNStatusDataOptions.SearchDepth.Empty ? "empty" : "infinity";
			var offline = options.Offline ? string.Empty : "-u";

			var result = ShellUtils.ExecuteCommand(SVN_Command, $"status --depth={depth} {offline} \"{SVNFormatPath(path)}\"", options.Timeout);

			if (!string.IsNullOrEmpty(result.error)) {

				if (!options.RaiseError)
					return Enumerable.Empty<SVNStatusData>();

				string displayMessage;
				bool isCritical = IsCriticalError(result.error, out displayMessage);

				if (!string.IsNullOrEmpty(displayMessage) && !Silent && m_LastDisplayedError != displayMessage) {
					Debug.LogError($"{displayMessage}\n\n{result.error}");
					m_LastDisplayedError = displayMessage;
					EditorUtility.DisplayDialog("SVN Error", displayMessage, "I will!");
				}

				if (isCritical) {
					throw new IOException($"Trying to get status for file {path} caused error:\n{result.error}!");
				} else {
					return Enumerable.Empty<SVNStatusData>();
				}
			}

			// If no info is returned for path, the status is normal. Reflect this when searching for Empty depth.
			if (options.Depth == SVNStatusDataOptions.SearchDepth.Empty) {

				if (options.Offline && string.IsNullOrWhiteSpace(result.output)) {
					return Enumerable.Repeat(new SVNStatusData() { Status = VCFileStatus.Normal, Path = path, LockDetails = LockDetails.Empty }, 1);
				}

				// If -u is used, additional line is added at the end:
				// Status against revision:     14
				if (!options.Offline && result.output.StartsWith("Status", StringComparison.Ordinal)) {
					return Enumerable.Repeat(new SVNStatusData() { Status = VCFileStatus.Normal, Path = path, LockDetails = LockDetails.Empty }, 1);
				}
			}

			return ExtractStatuses(result.output, options);
		}
예제 #4
0
		// Get offline status for a single file (non recursive). This won't make requests to the repository.
		// Will return valid status even if the file has nothing to show (has no changes).
		// If error happened, invalid status data will be returned (check statusData.IsValid).
		public static SVNStatusData GetStatus(string path)
		{
			// Optimization: empty depth will return nothing if status is normal.
			// If path is modified, added, deleted, unversioned, it will return proper value.
			var statusOptions = new SVNStatusDataOptions(SVNStatusDataOptions.SearchDepth.Empty);
			var statusData = GetStatuses(path, statusOptions).FirstOrDefault();

			// If no path was found, error happened.
			if (!statusData.IsValid) {
				// Fallback to unversioned as we don't touch them.
				statusData.Status = VCFileStatus.Unversioned;
			}

			return statusData;
		}
예제 #5
0
		// Get statuses of files based on the options you provide.
		// NOTE: data is returned ONLY for folders / files that has something to show (has changes, locks or remote changes).
		//		 If used with non-recursive option it will return single data with normal status (if non).
		public static SVNAsyncOperation<IEnumerable<SVNStatusData>> GetStatusesAsync(string path, bool recursive, bool offline, bool fetchLockDetails = true, int timeout = COMMAND_TIMEOUT)
		{
			// If default timeout, give some more time for online operations, just in case.
			if (timeout == COMMAND_TIMEOUT && !offline) {
				timeout *= 2;
			}

			var options = new SVNStatusDataOptions() {
				Depth = recursive ? SVNStatusDataOptions.SearchDepth.Infinity : SVNStatusDataOptions.SearchDepth.Empty,
				Timeout = timeout,
				RaiseError = false,
				Offline = offline,
				FetchLockOwner = fetchLockDetails,  // If offline, this is ignored.
			};

			return SVNAsyncOperation<IEnumerable<SVNStatusData>>.Start(op => GetStatuses(path, options));
		}
예제 #6
0
        // Executed in a worker thread.
        private void GatherSVNStatuses()
        {
            try {
                var statusOptions = new SVNStatusDataOptions()
                {
                    Depth          = SVNStatusDataOptions.SearchDepth.Infinity,
                    RaiseError     = false,
                    Timeout        = WiseSVNIntegration.COMMAND_TIMEOUT * 2,
                    Offline        = !SVNPreferencesManager.Instance.DownloadRepositoryChanges,
                    FetchLockOwner = true,
                };

                // Will get statuses of all added / modified / deleted / conflicted / unversioned files. Only normal files won't be listed.
                var statuses = WiseSVNIntegration.GetStatuses("Assets", statusOptions)
                               // Deleted svn file can still exist for some reason. Need to show it as deleted.
                               // If file doesn't exists, skip it as we can't show it anyway.
                               .Where(s => s.Status != VCFileStatus.Deleted || File.Exists(s.Path))
                               .Where(s => s.Status != VCFileStatus.Missing)
                               .ToList();

                for (int i = 0, count = statuses.Count; i < count; ++i)
                {
                    var statusData = statuses[i];

                    // Statuses for entries under unversioned directories are not returned. Add them manually.
                    if (statusData.Status == VCFileStatus.Unversioned && Directory.Exists(statusData.Path))
                    {
                        var paths = Directory.EnumerateFileSystemEntries(statusData.Path, "*", SearchOption.AllDirectories)
                                    .Where(path => !WiseSVNIntegration.IsHiddenPath(path))
                        ;

                        statuses.AddRange(paths
                                          .Select(path => path.Replace(WiseSVNIntegration.ProjectRoot, ""))
                                          .Select(path => new SVNStatusData()
                        {
                            Status = VCFileStatus.Unversioned, Path = path, LockDetails = LockDetails.Empty
                        })
                                          );
                    }
                }

                if (PendingUpdate == false)
                {
                    throw new Exception("SVN thread finished work but the update is over?");
                }

                m_PendingStatuses = statuses.ToArray();
            }
            // Most probably the assembly got reloaded and the thread was aborted.
            catch (System.Threading.ThreadAbortException) {
                System.Threading.Thread.ResetAbort();

                // Should always be true.
                if (PendingUpdate)
                {
                    m_PendingStatuses = new SVNStatusData[0];
                }
            } catch (Exception ex) {
                Debug.LogException(ex);

                // Should always be true.
                if (PendingUpdate)
                {
                    m_PendingStatuses = new SVNStatusData[0];
                }
            }
        }
예제 #7
0
		// Get statuses of files based on the options you provide.
		// NOTE: data is returned ONLY for folders / files that has something to show (has changes, locks or remote changes).
		//		 If used with non-recursive option it will return single data with normal status (if non).
		public static SVNAsyncOperation<IEnumerable<SVNStatusData>> GetStatusesAsync(string path, SVNStatusDataOptions options)
		{
			return SVNAsyncOperation<IEnumerable<SVNStatusData>>.Start(op => GetStatuses(path, options));
		}
예제 #8
0
		private static IEnumerable<SVNStatusData> ExtractStatuses(string output, SVNStatusDataOptions options)
		{
			using (var sr = new StringReader(output)) {
				string line;
				while ((line = sr.ReadLine()) != null) {

					var lineLen = line.Length;

					// Last status was deleted / added+, so this is telling us where it moved to / from. Skip it.
					if (lineLen > 8 && line[8] == '>')
						continue;

					// Tree conflict "local dir edit, incoming dir delete or move upon switch / update" or similar.
					if (lineLen > 6 && line[6] == '>')
						continue;

					// If there are any conflicts, the report will have two additional lines like this:
					// Summary of conflicts:
					// Text conflicts: 1
					if (line.StartsWith("Summary", StringComparison.Ordinal))
						break;

					// If -u is used, additional line is added at the end:
					// Status against revision:     14
					if (line.StartsWith("Status", StringComparison.Ordinal))
						break;

					// All externals append separate sections with their statuses:
					// Performing status on external item at '...':
					if (line.StartsWith("Performing status", StringComparison.Ordinal))
						continue;

					// If user has files in the "ignore-on-commit" list, this is added at the end plus empty line:
					// ---Changelist 'ignore-on-commit': ...
					if (string.IsNullOrEmpty(line))
						continue;
					if (line.StartsWith("---", StringComparison.Ordinal))
						break;

					// Rules are described in "svn help status".
					var statusData = new SVNStatusData();
					statusData.Status = m_FileStatusMap[line[0]];
					statusData.PropertyStatus = m_PropertyStatusMap[line[1]];
					statusData.LockStatus = m_LockStatusMap[line[5]];
					statusData.TreeConflictStatus = m_ConflictStatusMap[line[6]];
					statusData.LockDetails = LockDetails.Empty;

					// 7 columns statuses + space;
					int pathStart = 7 + 1;

					if (!options.Offline) {
						// + remote status + revision
						pathStart += 13;
						statusData.RemoteStatus = m_RemoteStatusMap[line[8]];
					}

					statusData.Path = line.Substring(pathStart);

					// NOTE: If you pass absolute path to svn, the output will be with absolute path -> always pass relative path and we'll be good.
					// If path is not relative, make it.
					//if (!statusData.Path.StartsWith("Assets", StringComparison.Ordinal)) {
					//	// Length+1 to skip '/'
					//	statusData.Path = statusData.Path.Remove(0, ProjectRoot.Length + 1);
					//}

					if (IsHiddenPath(statusData.Path))
						continue;


					if (!options.Offline && options.FetchLockOwner) {
						if (statusData.LockStatus != VCLockStatus.NoLock && statusData.LockStatus != VCLockStatus.BrokenLock) {
							statusData.LockDetails = FetchLockDetails(statusData.Path, options.Timeout, options.RaiseError);
						}
					}

					yield return statusData;
				}
			}
		}