Frodevs VCS | Custom Version Control System

Hello there! Frodev here!

Recently I’ve been coding a lot of things and have run into one problem. Whilst coding I will change the code and shut my studio, only to later needing to undo all that I did, but to bad I cant.


ENTER FRODEVS VCS

Frodevs VCS (Version Control System), allows you to save versions of any script and revert back to them at any time!


Features

  • Save: Saves the code in the selected script
  • Revert: Allows you to select a version of the script to revert it to!
  • Revert to last: Revert it to the last version!

Disclaimer

I’m aware there are other things like this, I just wanted to make my own.
Settings currently dont work, I wanted to get this out and forgot about them so I’ll ad em later.
EXPECT BUGS!!!


How to use

First get the plugin here
Next make sure to install it.

Now that you have it, all you need to do to get started is to select a script!
Now you can do whatever you want! (well that is within the plugins limitations)


If you have any feedback or find any bugs please feel free to reply to this post and let me know!
I am aware of the UI scaling problem, Ill have a fix for that later.

Well you’re welcome, sincerely,
~ Frodev ~

6 Likes

I think this is a great plugin with a solid concept. Version control in Roblox Studio can be challenging, and using third-party applications like Rojo + git often feels too finicky, especially with the need to open ports and such.

Personally, I would prefer running commands to commit and revert files instead of using a UI, as the UI can take up a significant portion of the viewing window. This approach could also pave the way for additional commands similar to git, such as git log and a branching system.

Overall, it’s a fantastic idea for a straightforward plugin that doesn’t require much configuration. Despite a few UI bugs, it serves well as a basic version control system and would recommend :slightly_smiling_face:

3 Likes

Hmm commands would be smart, in what way should I make said commands? Do you mean like a command bar where you put a command or an input on the keyboard? Thanks for the feedback!

3 Likes

Yes, as for implementing the commands, you have a few options:
Option one: Global Variable for command bar access
I experimented with using the global _G variable to access from the built in Roblox Studio command bar. Here’s some example code I wrote real quick to make a local plugin that would allow for potential commands in the form of a modulescript.

local toolbar = plugin:CreateToolbar("test for global commands")
local pluginButton = toolbar:CreateButton(
	"global Script", 
	"global script to test _G var", 
	"rbxassetid://8740888472") --random icon

--examples of methods to showcase file saving, reverting using global variable
--Usage here would be typing in the command bar(in studio), as in 
-- _G.commit_file(game.ServerScriptService.Script) (assuming there is a valid script in serverScriptservice


-- Services
local selectionService = game:GetService('Selection')
local storage = game:GetService('ServerStorage')
local SES = game:GetService('ScriptEditorService')

-- Setup versions folder
local function setupVersionsFolder()
	local versions = storage:FindFirstChild('Versions')

	if not versions then
		versions = Instance.new('Folder')
		versions.Name = 'Versions'
		versions.Parent = storage
	end

	return versions
end

local versions = setupVersionsFolder()

-- Main code
local selected

local commitHistory = {}

-- Functions
local function getSelected()
	local selectedObj = nil

	if selectionService:Get()[1] then
		selectedObj = game:GetService('Selection'):Get()[1]
	end

	return selectedObj
end

local function notify(MSG)
	if MSG == string then
		warn(string.upper(MSG))
	else
		warn(MSG)
	end
end

-- Global functions
function _G.commit_file(selected)
	if not versions:FindFirstChild(selected.Name) then
		local f = Instance.new('Folder')
		f.Name = selected.Name

		local cv = Instance.new('NumberValue')
		cv.Name = 'CV'
		cv.Value = 1.1
		cv.Parent = f

		f.Parent = versions
	end

	local currentV = versions:FindFirstChild(selected.Name)
	local cv = currentV:FindFirstChild('CV')

	local saveFile = Instance.new("Script")
	local code = SES:GetEditorSource(selected)
	SES:UpdateSourceAsync(saveFile, function(old)
		return code
	end)

	local newNum = cv.Value + 0.1
	local num = string.sub(tostring(newNum), 1, #string.split(tostring(math.floor(newNum)), "") + 2)
	saveFile.Name = num

	cv.Value = newNum
	saveFile.Parent = currentV

	table.insert(commitHistory, {name = currentV.Name, file = saveFile, timeStamp = os.time(), versionNum = cv.Value })

	warn("NEW VERSION CREATED, V:"..num)
	warn('NEW VERSION CREATED: ', saveFile)
end

function _G.revert_file(selected)
	if versions:FindFirstChild(selected.Name) then
		local vc = versions:FindFirstChild(selected.Name)
		local newNum = vc:FindFirstChild('CV').Value -- 0.1
		local num = string.sub(tostring(newNum), 1, #string.split(tostring(math.floor(newNum)), "") + 2)
		local last = vc:FindFirstChild(num)

		if last then
			warn('Loading')
			local code = SES:GetEditorSource(last)
			local success, err = pcall(function()
				SES:UpdateSourceAsync(selected, function(old)
					return code
				end)
			end)

			if success then
				notify('Successfully loaded version '..num)
				warn('Successfully loaded version '..num)
			else
				notify("FAILED: "..err)
				warn(err)
			end
		else
			notify("NO VERSION TO REVERT TO.")
			warn('NO VERSION TO REVERT TO')
		end
	end
end

function _G.show_history()
	if #commitHistory == 0 then
		notify("No commit history available")
	else
		notify("Current history:")
		for _, entry in ipairs(commitHistory) do
			notify(string.format("Name: %s, Version: %.1f, Timestamp: %s", entry.name, entry.versionNum, os.date("%c", entry.timeStamp)))
		end
	end
end


function _G.clear_history()
	if #commitHistory == 0 then
		notify("No commit history to clear")
	else
		commitHistory = {}
		notify("Commit history cleared")
	end
end

Option 2: Using custom GUI to create a textbox/textarea
This is some example code you could use to create a textbox in the coregui for entering commands too.

local coreGUI = game:GetService('CoreGui')

local GUI = Instance.new("ScreenGui")
GUI.Parent = coreGUI

local textBox = Instance.new("TextBox")
textBox.Size = UDim2.new(0, 300, 0, 50)
textBox.Position = UDim2.new(0.5, -150, 0.5, -25)
textBox.PlaceholderText = "Enter command here..."
textBox.Parent = GUI

-- Command Handling
textBox.FocusLost:Connect(function(enterPressed)
	if enterPressed then
		local command = textBox.Text
		textBox.Text = "" 

		local selected = getSelected()
		if command == "commit" then
			_G.commit_file(selected)
		elseif command == "revert" then
			_G.revert_file(selected)
		elseif command == "show history" then
			_G.show_history()
		elseif command == "clear history" then
			_G.clear_history()
		else
			notify("Unknown command: " .. command)
		end
	end
end)

You could also probably use hotkeys for easier usage of commands.

2 Likes

Welp Ill try this out later! Good thing if it doesnt work I can easily revert it now

hehehehe

1 Like