Forums

Articles
Create
cancel
Showing results for 
Search instead for 
Did you mean: 

How to keep the user's config when updating SourceTree Enterprise?

Jonas Denter
October 28, 2024

We want to update our installations of SourceTree Enterprise (on Windows 10 and 11; ST version 3.4.18) to version 3.4.20.

When rolling out the new version we observed that the user.config found under 

%userprofile%\AppData\Local\Atlassian\SourceTree.exe_Url_someHashLikeString\3.4.18.0 is not transferred over to \3.4.20.0 which results in all settings reverting back to default.

We know that it is possible to ship a pre-configured SourceTree.exe.config with modified settings, but this is only useful for some general settings, not for all of them.

 

So the question is: How can we make sure our users don't lose their configurations every time we update SourceTree?

2 answers

0 votes
Jonas Denter
March 25, 2026

Back then I had spent way too much time on a solution that is acceptable, sort of.

It is a PowerShell script that will be executed whenever a user logs on. It checks for a registry key and compares it to the currently installed version of SourceTree and will continue if the current version is newer. It then performs a few checks and copies over the config from the last version, if applicable.

This way of doing it has a few shortcomings, though:

  • The script is executed at logon. If an update happens while the user is logged on (which happens more often than I expected), the user will likely launch the new version of SourceTree without their config. How to solve that? Probably package the .msi with a post-install script, but the script would then need to run for every user on that machine with a valid SourceTree config... And besides, our IT department would not let me have any of that. (Too complicated?)
  • The directory suffix (hash of installation type "Standalone vs. Enterprise" and installation folder, or something along those lines) is hard-coded. So whenever the installation folder changes, the script needs to be updated accordingly. I've got no idea, how I would predict the suffix. And good luck transferring configs between different hashes... 
  • The current handling of edge cases is not ideal. The GridView utilized in this approach is neither visually appealing nor does it clearly display its purpose. Other approaches would have required additional cmdlets, though.

Overall, we're contemplating reverting to individual user-scoped installations and just have the users be responsible for their own housekeeping.

For reference, the script I referred to:

# Copy SourceTree Config on logon, only requires user-level privileges
#
# By Jonas Denter 11.11.2024
# Lauer & Weiss GmbH
#

# add a module to facilitate searching the registry
Import-Module "$PSScriptRoot\search-registry.ps1" -Force

# where the this script will write its log to:
$Logfile = "$env:USERPROFILE\sourceTreeConfig.log"

# where in the registry this script will persist its status for subsequent calls
# results in: HKCU:\SOFTWARE\LuW\SourceTreeUpdateScript.VersionLastExecuted
$RegBase = "HKCU:\SOFTWARE"
$RegLuW = "LuW"
$RegSubContainer = "SourceTreeUpdateScript"
$KeyName = "VersionLastExecuted"
# where the SourceTree user config is expected to be found (LOCAL):
$ConfigBaseFolderLocal = "$env:LOCALAPPDATA\Atlassian\SourceTree.exe_Url_yjhzxxhcu2ommegrdorjf3q1oqllqxiq"
# where the SourceTree user config is expected to be found (Roaming):
$ConfigBaseFolderRoaming = "$env:APPDATA\Atlassian\SourceTree.exe_Url_yjhzxxhcu2ommegrdorjf3q1oqllqxiq"

Function LogWrite
{
Param ([string]$Message)

Write-Output $Message

$Stamp = (Get-Date).toString("yyyy/MM/dd HH:mm:ss")
$logstring = "$Stamp $Message"
Add-content $Logfile -value $logstring
}

# try to find the "Installed Apps" entry for SourceTree
$regSearchResult = Search-Registry -Path "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Installer\UserData\*\Products\" -Recurse -ValueDataRegex "Sourcetree"
# bail if search returns empty -> SourceTree not installed?
if (-Not ($regSearchResult)) {
LogWrite "Registry entry not found. Is SourceTree not installed?"
Exit 0
}
$stInstallationContainer = $regSearchResult.Key

# if the entry exists, it will contain the key "DisplayVersion", which has the version information for the latest SourceTree installation - thus exactly what is needed here
$versionKeyName = "DisplayVersion"
$targetVersion = [System.Version]$stInstallationContainer.GetValue($versionKeyName, $null)
# bail if DisplayVersion does not exist - script likely needs to be adapted then
if (-Not ($targetVersion)) {
LogWrite ("Registry key '" + $versionKeyName + "' not found. Please investigate.")
Exit 0
}

LogWrite ("Latest installed version of SourceTree: " + $targetVersion.ToString())

# test for existence of registry containers and create them if needed
$LuWContainer = ("$RegBase\$RegLuW")
if (-not (Test-Path $LuWContainer)) {
New-Item -Path $RegBase -Name $RegLuW
}

$SubCont = ("$LuWContainer\$RegSubContainer")
if (Test-Path $SubCont) {
$contPath = Get-Item -Path $SubCont
} else {
$contPath = New-Item -Path $LuWContainer -Name $RegSubContainer
}

# test for existence of registry key and create it if needed
if ($null -eq $contPath.GetValue($KeyName, $null)) {
LogWrite ("Key '" + $KeyName + "' does not exist in container " + $SubCont + ". Creating it now.")
$newItemPropertySplat = @{
Path = $contPath.PsPath
Name = $KeyName
PropertyType = 'String'
Value = "3.4.18.0"
}
New-ItemProperty @newItemPropertySplat
}

# retrieve the version this script last executed for
$lastRunVersion = [System.Version]$contPath.GetValue($KeyName, "0.0.0")
LogWrite ("The SourceTree config has last been copied for version: " + $lastRunVersion.ToString())

if ($lastRunVersion -ge $targetVersion) {
LogWrite ("VersionLastExecuted exceeds VersionCurrentlyInstalled. Exiting now.")
Exit 0
}

# find available config versions
$configFolders = Get-ChildItem -Directory -Path $ConfigBaseFolderLocal
$configVersions = $configFolders.Name | ForEach-Object {[System.Version]$_}

# filter out all versions greater than or equal to the current version, and sort the remaining entries
$configVersionsBelowTarget = $configVersions | Where-Object { $_ -lt $targetVersion }
$configVersionsBelowTarget = $configVersionsBelowTarget | Sort-Object -Descending

# if the target (=latest installed) version already has a config folder, we need to ask
# as this means either the script has already run or the user created the folder
$targetConfigExists = $configVersions -contains $targetVersion

# if the target version already exists, ask what to do,
# otherwise just take the largest version below targetVersion as source
if ($targetConfigExists) {
LogWrite "The current version of SourceTree already has a config folder. What now?"
# User selection:
# - Via Listbox: Nicer, can be customized to make clear what is asked
# - Out-GridView: No work needed, but not as clear
$configVersions = $configVersions | Sort-Object -Descending
$sourceVersion = $configVersions | Out-GridView -OutputMode Single -Title "Select version for SourceTree's old config:"
if ($null -ne $sourceVersion) {
LogWrite ("User selection: '" + $sourceVersion.ToString() + "'" )
}
} else {
$sourceVersion = $configVersionsBelowTarget | Select-Object -First 1
}

if ($null -eq $sourceVersion) {
LogWrite "No source folder selected or no source folders available. Exiting now."
} elseif ($sourceVersion -eq $targetVersion) {
LogWrite "Source folder equals target folder. Nothing to do."
} else {
# copy source folder and rename it to target version
# AppData\Local:
$sourceFolder = $ConfigBaseFolderLocal + "\" + $sourceVersion.ToString()
$targetFolder = $ConfigBaseFolderLocal + "\" + $targetVersion.ToString()
# Workaround for an inconsistency in Copy-Item:
# New-Item will create the folder or just return it if it already exists
# see https://github.com/PowerShell/PowerShell/issues/2934
Copy-Item -Path "$sourceFolder\*" -Destination (New-Item -Force -ItemType Directory $targetFolder) -Recurse -Force

# AppData\Roaming:
$sourceFolder = $ConfigBaseFolderRoaming + "\" + $sourceVersion.ToString()
$targetFolder = $ConfigBaseFolderRoaming + "\" + $targetVersion.ToString()
Copy-Item -Path "$sourceFolder\*" -Destination (New-Item -Force -ItemType Directory $targetFolder) -Recurse -Force

LogWrite ("Copied folder " + $sourceVersion.ToString() + " to " + $targetVersion.ToString() )
}

# Update registry entry with current version
Set-ItemProperty -Path $contPath.PSPath -Name $KeyName -Value $targetVersion.ToString()
LogWrite ("Updated " + $contPath + "\" + $KeyName + " from " + $lastRunVersion.ToString() + " to " + $targetVersion.ToString() )

 It needs an additional cmdlet search-registry.ps1, which I sourced from: StackOverflow 

Hope this will be helpful to anyone.

0 votes
Tinker Fadoua
Community Champion
March 18, 2026

Welcome to the Atlassian Community @Jonas Denter !

I know it has been some time since you posted your question.

Please let us know if you were able to fix the issue.

 

Jonas Denter
March 25, 2026

I've written up an answer that describes how we "solved" the problem. But as it can be considered a workaround at best, I'll leave the question unanswered.

Suggest an answer

Log in or Sign up to answer
TAGS
AUG Leaders

Atlassian Community Events