Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

GetEffectiveGitSetting treats non 0 results as failures #11145

Open
vbjay opened this issue Aug 12, 2023 · 16 comments · May be fixed by #11146
Open

GetEffectiveGitSetting treats non 0 results as failures #11145

vbjay opened this issue Aug 12, 2023 · 16 comments · May be fixed by #11146
Labels
🚧 status: in progress Issues which have associated PRs

Comments

@vbjay
Copy link
Contributor

vbjay commented Aug 12, 2023

Environment

  • Git Extensions 33.33.33
  • Build 79a312b
  • Git 2.41.0.windows.1
  • Microsoft Windows NT 10.0.22621.0
  • .NET 6.0.21
  • DPI 96dpi (no scaling)
  • Portable: False
  • Microsoft.WindowsDesktop.App Versions
    Microsoft.WindowsDesktop.App 3.1.8 [C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App]
    Microsoft.WindowsDesktop.App 3.1.32 [C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App]
    Microsoft.WindowsDesktop.App 5.0.4 [C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App]
    Microsoft.WindowsDesktop.App 5.0.9 [C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App]
    Microsoft.WindowsDesktop.App 5.0.17 [C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App]
    Microsoft.WindowsDesktop.App 6.0.5 [C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App]
    Microsoft.WindowsDesktop.App 6.0.15 [C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App]
    Microsoft.WindowsDesktop.App 6.0.19 [C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App]
    Microsoft.WindowsDesktop.App 6.0.21 [C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App]
    Microsoft.WindowsDesktop.App 7.0.5 [C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App]
    Microsoft.WindowsDesktop.App 7.0.10 [C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App]

Issue description

As seen in https://git-scm.com/docs/git-config#_description it is not an error to get an exit code of non 0. Right now we get an exception because of the changes to GetOutput and treating all non 0 exit codes as exceptions instead of handing the result.

This command will fail with non-zero status upon error. Some exit codes are:

The section or key is invalid (ret=1),

no section or name was provided (ret=2),

the config file is invalid (ret=3),

the config file cannot be written (ret=4),

you try to unset an option which does not exist (ret=5),

you try to unset/set an option for which multiple lines match (ret=5), or

you try to use an invalid regexp (ret=6).

Not understanding exit codes as non errors is a failure on executable handling. Found during gpg work on a clean machine ant it looking for gpg.Program and commit.gpgSign and such. Which when not set, exit code is 1. Which tells you that value is not set.

Steps to reproduce

Change the name and email fetching in formCommit to get usser.Value

Did this work in previous version of GitExtensions?

Maybe before the changes @mstv and @gerhardol did with changing the way executable output is handled with GetOutput from the executable.

Diagnostics

No response

@ghost ghost added the 🚧 status: in progress Issues which have associated PRs label Aug 12, 2023
@gerhardol
Copy link
Member

All commands where 0 is not a failure explicitly handles this. There are probably more of these commands than those that fails.

(And it is mstv that has the credit of introducing this handling, I have though been involved in handling git errors. A number of unknown issues were fixed this way.)

@mstv
Copy link
Member

mstv commented Aug 12, 2023

Not understanding exit codes as non errors is a failure on executable handling.

Which of these exit codes is not an error for string GetEffectiveGitSetting(...)? None.

If you need a TryGetEffectiveGitSetting, you can add one. And reuse its implementation for GetEffectiveGitSetting.

@vbjay
Copy link
Contributor Author

vbjay commented Aug 12, 2023

Not understanding exit codes as non errors is a failure on executable handling.

Which of these exit codes is not an error for string GetEffectiveGitSetting(...)? None.

If you need a TryGetEffectiveGitSetting, you can add one. And reuse its implementation for GetEffectiveGitSetting.

Exactly my point but if you will take the time to do something like add a test that shows current implementation raises an exception if config key is not set. Try adding a test that looks for either a valid git config key found from git help config or a random string like git config cat.dog that in your system is unset. Try adding a line to the commit button temporarily that does this. No matter the case, git config will gladly return a blank value. Git config doesn't limit you to specific keys. It just handles . notation to help the user manage sections in config files. I am vehemently against one implementation providing errors and no helpful info and another method that actually provides a value like if you ran in bash a blank value and a status. In bash I can get the exit code but if I store the result in a variable I get an empty value. Instead of getting exceptions from non 0 exit codes that actually provide your value was unset. Then where the use is can decide oh if unset behave this way or if config file cannot be written if we used git config to write a value. The plumbing is there. You now don't get an exception and instead get an object with the info needed to act if desired or just accept the blank value if desired. I actually put forth thought and reasoning behind this and how to do it right.

@mstv
Copy link
Member

mstv commented Aug 12, 2023

Again, if you need a TryGet, implement one.

If the error from the Get function is not clear enough, it can be improved. Users of GetEffectiveGitSetting(...) expect the existence of the config value. If it does not exist, an exception shall inform the user. No boiler-plate code "has it succeeded or not" shall be written for all usages.

BTW: "config file cannot be written" is completely off-topic for a Get function.

@vbjay
Copy link
Contributor Author

vbjay commented Aug 12, 2023

I don't think you see my point. I will walk you through it.

image

  • Git Extensions 33.33.33
  • Build 79a312b (Dirty)
  • Git 2.41.0.windows.1
  • Microsoft Windows NT 10.0.22621.0
  • .NET 6.0.21
  • DPI 96dpi (no scaling)
  • Portable: False

Exit code: 1
Command: C:\Program Files\Git\bin\git.exe
Arguments: config --includes --get cat.dog
Working directory: C:\git\gitextensions\

GitExtUtils.ExternalOperationException
 ---> System.Exception
   --- End of inner exception stack trace ---
   at GitCommands.ExecutableExtensions.GetOutputAsync(IExecutable executable, ArgumentString arguments, Byte[] input, Encoding outputEncoding, CommandCache cache, Boolean stripAnsiEscapeCodes) in C:\git\gitextensions\GitCommands\Git\ExecutableExtensions.cs:line 129
   at Microsoft.VisualStudio.Threading.JoinableTask.CompleteOnCurrentThread()
   at Microsoft.VisualStudio.Threading.JoinableTask`1.CompleteOnCurrentThread()
   at GitCommands.ExecutableExtensions.GetOutput(IExecutable executable, ArgumentString arguments, Byte[] input, Encoding outputEncoding, CommandCache cache, Boolean stripAnsiEscapeCodes) in C:\git\gitextensions\GitCommands\Git\ExecutableExtensions.cs:line 43
   at GitCommands.GitModule.GetEffectiveGitSetting(String setting, Boolean cache) in C:\git\gitextensions\GitCommands\Git\GitModule.cs:line 2476
   at GitUI.CommandsDialogs.FormCommit.<>c__DisplayClass138_0.<CheckForStagedAndCommit>g__DoCommit|2() in C:\git\gitextensions\GitUI\CommandsDialogs\FormCommit.cs:line 1322
   at GitUI.CommandsDialogs.FormCommit.<>c__DisplayClass138_0.<CheckForStagedAndCommit>b__0() in C:\git\gitextensions\GitUI\CommandsDialogs\FormCommit.cs:line 1228
   at GitUI.CommandsDialogs.FormCommit.BypassFormActivatedEventHandler(Action action) in C:\git\gitextensions\GitUI\CommandsDialogs\FormCommit.cs:line 2692
   at GitUI.CommandsDialogs.FormCommit.CheckForStagedAndCommit(Boolean push) in C:\git\gitextensions\GitUI\CommandsDialogs\FormCommit.cs:line 1224
   at GitUI.CommandsDialogs.FormCommit.ExecuteCommitCommand() in C:\git\gitextensions\GitUI\CommandsDialogs\FormCommit.cs:line 2797
   at GitUI.CommandsDialogs.FormCommit.CommitClick(Object sender, EventArgs e) in C:\git\gitextensions\GitUI\CommandsDialogs\FormCommit.cs:line 1215
   at System.Windows.Forms.Control.OnClick(EventArgs e)
   at System.Windows.Forms.Button.OnClick(EventArgs e)
   at System.Windows.Forms.Button.OnMouseUp(MouseEventArgs mevent)
   at System.Windows.Forms.Control.WmMouseUp(Message& m, MouseButtons button, Int32 clicks)
   at System.Windows.Forms.Control.WndProc(Message& m)
   at System.Windows.Forms.ButtonBase.WndProc(Message& m)
   at System.Windows.Forms.Button.WndProc(Message& m)
   at System.Windows.Forms.Control.ControlNativeWindow.OnMessage(Message& m)
   at System.Windows.Forms.Control.ControlNativeWindow.WndProc(Message& m)
   at System.Windows.Forms.NativeWindow.Callback(IntPtr hWnd, WM msg, IntPtr wparam, IntPtr lparam)
diff --git a/GitUI/CommandsDialogs/FormCommit.cs b/GitUI/CommandsDialogs/FormCommit.cs
index 8ee22871b..980fa7f5e 100644
--- a/GitUI/CommandsDialogs/FormCommit.cs
+++ b/GitUI/CommandsDialogs/FormCommit.cs
@@ -1319,6 +1319,9 @@ bool ConfirmAndStageAllUnstaged()
 
             void DoCommit()
             {
+                var catDog = Module.GetEffectiveGitSetting("cat.dog");
+                Console.WriteLine($"cat.dog:'{catDog}'");
+
                 if (Module.InTheMiddleOfConflictedMerge())
                 {
                     MessageBox.Show(this, _mergeConflicts.Text, _mergeConflictsCaption.Text, MessageBoxButtons.OK, MessageBoxIcon.Error);

This is DEAD wrong. The config result is a blank string and exit code 1 says value unset. This is NOT an exception. I got the expected result. It is valid and did not fail and is not exceptional state. My fix does the following

  • Does not raise exceptions with the exit codes provided
  • Provides a status enum that can be checked and the user can react how they wish if they are wanting to maybe not use blank or wants to write a lot more clear code like if(result.Status == ....Unset)...
  • Having two functions that return a config and one have an exception that provides no helpful what is 1 what is 2... Is beyond foolish. I change the api on purpose so that it is consistent and everwhere being used can now get not just status and value and... but can then react to it. Even the state of status being null which would mean the exit code was outside of known config exit codes and could be a potential exceptional state
    image

I am against creating a try... function and leaving another function that just exceptions. My way provides status and allows for exceptional handling if needed with null status value and will prevent users reporting x error when it was really just that their config value was unset.

BTW: "config file cannot be written" is completely off-topic for a Get function.

Setting config values. Note the exit codes 3, 4, 5. I know I am not dealing with setting values but I did provide the values just the same so that they could be used.

@vbjay
Copy link
Contributor Author

vbjay commented Aug 12, 2023

With my change
image No exceptions and infotmation that config key was unset that could be coded for.

@vbjay
Copy link
Contributor Author

vbjay commented Aug 12, 2023

Yes I can add the config name and potentially when retrieved. There is room for metadata around this.

@mstv
Copy link
Member

mstv commented Aug 12, 2023

What is unclear with

Exit code: 1
Command: C:\Program Files\Git\bin\git.exe
Arguments: config --includes --get cat.dog
Working directory: C:\git\gitextensions\

?

What you want, is a TryGet. But you call Get! Get presumes that the value is set -- perhaps to an empty string. So it is different function for a different purpose.
With TryGet, one can distinguish unset values from empty ones, which may be important.

@mstv
Copy link
Member

mstv commented Aug 12, 2023

allows for exceptional handling if needed

That is the same error-hiding attitude as .ExitCode ?? 0.
What if one "forgets" to evaluate the provided status?

prevent users reporting x error when it was really just that their config value was unset.

That's why it is an ExternalOperationException, for which there is no "Report bug" button in the TaskDialog.
Perhaps, we should also disable the "Report bug" button in the BugReportForm in case of a ExternalOperationException.

@vbjay
Copy link
Contributor Author

vbjay commented Aug 12, 2023

Why raise a dialog saying it is an exception? Why say the blank value is wrong. Exit code of 1 means config value is unset and if specific state that user needs to respond...code it in a case or if...

image

@mstv
Copy link
Member

mstv commented Aug 12, 2023

Again: Get presumes that the value is set. You want a different function. Then emphasize that it can fail without throwing an exception. That's why TryGet.

@vbjay
Copy link
Contributor Author

vbjay commented Aug 12, 2023

Do you run git try-getconfig? No. I am doing same 5hing here. Run git config and get the output and also store the result from exit code. Then use this result value as needed where you called. Exit code 1 is NOT an exceptional state. If git config blew up and returned say exit code 99. Yes that would be a exception. We can definitely show that. What I don't like is this get setting and it blows up vs a result that will always provide you details and each caller can deal as needed.

@mstv
Copy link
Member

mstv commented Aug 12, 2023

Have you ever used a Dictionary<K, V>?

@vbjay
Copy link
Contributor Author

vbjay commented Aug 12, 2023

Sure I have worked with a generic dictionary. But I was working with potential more fields like contig key asked for just in case we do a whole git config fetch down the road. That is where I would do a Dictionary<string, MySettingType). I was more using this to define the result with OOP principles like being able to add ConfigKey, Value, Status, WhenRetrieved...Whatever down the road. I knew both of you would push back because you a don't quite seem to get exit codes and the fact that they don't always mean error and that I am pushing for consistency and one result of a git config value that provides all the info from one method. I hate the idea of a user trying to figure out do I do TryGetEffectiveGtSetting vs the other thing. Yes if exit code isn't defined in enum.. sure I can see an exception being thrown.. Frankly it is too easy to goof the Git Version of GetEffectiveGitSettkng vs the GetEffectiveSetting. But that is another topic. Yes expose git errors where needed but read the exit codes. 1 is clearly my example of cat.dog and attempting to retrieve it. Good example is if you don't have gpg.program set. In my gpg work and without me changing functionality, you get an error dialog with exit code 1. The program should internally handle that and based on what is desired ( just accept blank value, write special case code to handle this state...). Exceptions are for Exceptional state. An unset git config value is understood by everyone to return a blank value. Not everyone understands 1 is unset. Our program can help by going. I see that the value is blank and the status is unset... I can not create an exception and instead provide the blanks and Unset status. If I as a non admin user tried to run code that tried to set a system level git config value, our program could look at the status and see maybe a 3 or 4 result.

http://www.codinghell.ch/blog/2013/03/31/how-and-when-not-to-use-exceptions/#:~:text=Don't%20use%20exceptions%20to,fields%20for%20flow%20control%20instead. If I can see the status and skip the whole exception crud, I would rather do that. Yes can add throw where needed but if 1, not an exception...Just return blank and unset to user. No need for exception building and throwing.

@vbjay
Copy link
Contributor Author

vbjay commented Aug 12, 2023

Get in the context of git config does NOT bomb on unset is my main point. Your point about Get assumes values is set is silly. I have git config values that you probably don't. I would expect it to work where if unset it provides the blank. Pull my branch from the pull request and run the test
image
Look at the output. Note the ConfigKeyInvalidOrNotSet section. I added some fake config keys to always have that section in the test. I'll rewrite the test on master and show you the result. Hopefully you will see I am trying to get you to change the assumption.

ConfigKeyInvalidOrNotSet
	add.ignoreErrors
	add.interactive.useBuiltin
	advice.addEmbeddedRepo
	advice.addEmptyPathspec
	advice.addIgnoredFile
	advice.amWorkDir
	advice.ambiguousFetchRefspec
	advice.checkoutAmbiguousRemoteBranchName
	advice.commitBeforeMerge
	advice.detachedHead
	advice.diverging
	advice.fetchShowForcedUpdates
	advice.graftFileDeprecated
	advice.ignoredHook
	advice.implicitIdentity
	advice.nameTooLong
	advice.nestedTag
	advice.objectNameWarning
	advice.pushAlreadyExists
	advice.pushFetchFirst
	advice.pushNeedsForce
	advice.pushNonFFCurrent
	advice.pushNonFFMatching
	advice.pushNonFastForward
	advice.pushRefNeedsUpdate
	advice.pushUnqualifiedRefName
	advice.pushUpdateRejected
	advice.resetNoRefresh
	advice.resolveConflict
	advice.rmHints
	advice.sequencerInUse
	advice.setUpstreamFailure
	advice.skippedCherryPicks
	advice.statusAheadBehindWarning
	advice.statusHints
	advice.statusUoption
	advice.submoduleAlternateErrorStrategyDie
	advice.submodulesNotUpdated
	advice.suggestDetachingHead
	advice.updateSparsePath
	advice.useCoreFSMonitorConfig
	advice.waitingForEditor
	alias.*
	am.keepcr
	am.threeWay
	apply.ignoreWhitespace
	apply.whitespace
	author.email
	author.name
	blame.blankBoundary
	blame.coloring
	blame.date
	blame.ignoreRevsFile
	blame.markIgnoredLines
	blame.markUnblamableLines
	blame.showEmail
	blame.showRoot
	branch.<name>.description
	branch.<name>.merge
	branch.<name>.mergeOptions
	branch.<name>.pushRemote
	branch.<name>.rebase
	branch.<name>.remote
	branch.autoSetupMerge
	branch.autoSetupRebase
	branch.sort
	browser.<tool>.cmd
	browser.<tool>.path
	bundle.*
	bundle.<id>.*
	bundle.<id>.uri
	bundle.heuristic
	bundle.mode
	bundle.version
	checkout.defaultRemote
	checkout.guess
	checkout.thresholdForParallelism
	checkout.workers
	clean.requireForce
	clone.defaultRemoteName
	clone.filterSubmodules
	clone.rejectShallow
	color.advice
	color.advice.hint
	color.blame.highlightRecent
	color.blame.repeatedLines
	color.branch
	color.branch.current
	color.branch.local
	color.branch.plain
	color.branch.remote
	color.branch.reset
	color.branch.upstream
	color.branch.worktree
	color.decorate.HEAD
	color.decorate.branch
	color.decorate.grafted
	color.decorate.remoteBranch
	color.decorate.stash
	color.decorate.tag
	color.diff
	color.diff.commit
	color.diff.context
	color.diff.contextBold
	color.diff.contextDimmed
	color.diff.frag
	color.diff.func
	color.diff.meta
	color.diff.new
	color.diff.newBold
	color.diff.newDimmed
	color.diff.newMoved
	color.diff.newMovedAlternative
	color.diff.newMovedAlternativeDimmed
	color.diff.newMovedDimmed
	color.diff.old
	color.diff.oldBold
	color.diff.oldDimmed
	color.diff.oldMoved
	color.diff.oldMovedAlternative
	color.diff.oldMovedAlternativeDimmed
	color.diff.oldMovedDimmed
	color.diff.plain
	color.diff.whitespace
	color.grep
	color.grep.column
	color.grep.context
	color.grep.filename
	color.grep.function
	color.grep.lineNumber
	color.grep.match
	color.grep.matchContext
	color.grep.matchSelected
	color.grep.selected
	color.grep.separator
	color.interactive
	color.interactive.error
	color.interactive.header
	color.interactive.help
	color.interactive.plain
	color.interactive.prompt
	color.interactive.reset
	color.pager
	color.push
	color.push.error
	color.remote
	color.remote.error
	color.remote.hint
	color.remote.success
	color.remote.warning
	color.showBranch
	color.status
	color.status.added
	color.status.branch
	color.status.changed
	color.status.header
	color.status.localBranch
	color.status.noBranch
	color.status.remoteBranch
	color.status.unmerged
	color.status.untracked
	color.status.updated
	color.transport
	color.transport.rejected
	color.ui
	column.branch
	column.clean
	column.status
	column.tag
	column.ui
	commit.cleanup
	commit.status
	commit.template
	commit.verbose
	commitGraph.generationVersion
	commitGraph.maxNewFilters
	commitGraph.readChangedPaths
	committer.email
	committer.name
	completion.commands
	core.abbrev
	core.alternateRefsCommand
	core.alternateRefsPrefixes
	core.askPass
	core.attributesFile
	core.bigFileThreshold
	core.checkRoundtripEncoding
	core.checkStat
	core.commentChar
	core.compression
	core.createObject
	core.deltaBaseCacheLimit
	core.eol
	core.excludesFile
	core.filesRefLockTimeout
	core.fsmonitor
	core.fsmonitorHookVersion
	core.fsync
	core.fsyncMethod
	core.fsyncObjectFiles
	core.gitProxy
	core.hideDotFiles
	core.hooksPath
	core.ignoreStat
	core.longpaths
	core.looseCompression
	core.multiPackIndex
	core.notesRef
	core.packedGitLimit
	core.packedGitWindowSize
	core.packedRefsTimeout
	core.pager
	core.precomposeUnicode
	core.preferSymlinkRefs
	core.protectHFS
	core.protectNTFS
	core.quotePath
	core.restrictinheritedhandles
	core.sharedRepository
	core.sparseCheckout
	core.sparseCheckoutCone
	core.splitIndex
	core.sshCommand
	core.trustctime
	core.unsetenvvars
	core.untrackedCache
	core.useReplaceRefs
	core.warnAmbiguousRefs
	core.whitespace
	core.worktree
	credential.<url>.*
	credential.useHttpPath
	credential.username
	credentialCache.ignoreSIGHUP
	credentialStore.lockTimeoutMS
	diff.<driver>.binary
	diff.<driver>.cachetextconv
	diff.<driver>.command
	diff.<driver>.textconv
	diff.<driver>.wordRegex
	diff.<driver>.xfuncname
	diff.algorithm
	diff.autoRefreshIndex
	diff.colorMoved
	diff.colorMovedWS
	diff.context
	diff.dirstat
	diff.external
	diff.ignoreSubmodules
	diff.indentHeuristic
	diff.interHunkContext
	diff.mnemonicPrefix
	diff.noprefix
	diff.orderFile
	diff.relative
	diff.renameLimit
	diff.renames
	diff.statGraphWidth
	diff.submodule
	diff.suppressBlankEmpty
	diff.wordRegex
	diff.wsErrorHighlight
	difftool.<tool>.cmd
	difftool.<tool>.path
	difftool.guiDefault
	difftool.trustExitCode
	extensions.objectFormat
	extensions.worktreeConfig
	fastimport.unpackLimit
	feature.*
	feature.experimental
	feature.manyFiles
	fetch.bundleCreationToken
	fetch.bundleURI
	fetch.fsck.<msg-id>
	fetch.fsck.skipList
	fetch.fsckObjects
	fetch.negotiationAlgorithm
	fetch.output
	fetch.parallel
	fetch.pruneTags
	fetch.recurseSubmodules
	fetch.showForcedUpdates
	fetch.unpackLimit
	fetch.writeCommitGraph
	filter.<driver>.clean
	filter.<driver>.smudge
	format.attach
	format.cc
	format.coverFromDescription
	format.coverLetter
	format.encodeEmailHeaders
	format.filenameMaxLength
	format.forceInBodyFrom
	format.from
	format.headers
	format.mboxrd
	format.noprefix
	format.notes
	format.numbered
	format.outputDirectory
	format.pretty
	format.signOff
	format.signature
	format.signatureFile
	format.subjectPrefix
	format.suffix
	format.thread
	format.to
	format.useAutoBase
	fsck.badDate
	fsck.badDateOverflow
	fsck.badEmail
	fsck.badFilemode
	fsck.badName
	fsck.badObjectSha1
	fsck.badParentSha1
	fsck.badTagName
	fsck.badTimezone
	fsck.badTree
	fsck.badTreeSha1
	fsck.badType
	fsck.duplicateEntries
	fsck.emptyName
	fsck.extraHeaderEntry
	fsck.fullPathname
	fsck.gitattributesBlob
	fsck.gitattributesLarge
	fsck.gitattributesLineLength
	fsck.gitattributesMissing
	fsck.gitattributesSymlink
	fsck.gitignoreSymlink
	fsck.gitmodulesBlob
	fsck.gitmodulesLarge
	fsck.gitmodulesMissing
	fsck.gitmodulesName
	fsck.gitmodulesParse
	fsck.gitmodulesPath
	fsck.gitmodulesSymlink
	fsck.gitmodulesUpdate
	fsck.gitmodulesUrl
	fsck.hasDot
	fsck.hasDotdot
	fsck.hasDotgit
	fsck.mailmapSymlink
	fsck.missingAuthor
	fsck.missingCommitter
	fsck.missingEmail
	fsck.missingNameBeforeEmail
	fsck.missingObject
	fsck.missingSpaceBeforeDate
	fsck.missingSpaceBeforeEmail
	fsck.missingTag
	fsck.missingTagEntry
	fsck.missingTaggerEntry
	fsck.missingTree
	fsck.missingType
	fsck.missingTypeEntry
	fsck.multipleAuthors
	fsck.nulInCommit
	fsck.nulInHeader
	fsck.nullSha1
	fsck.skipList
	fsck.treeNotSorted
	fsck.unknownType
	fsck.unterminatedHeader
	fsck.zeroPaddedDate
	fsck.zeroPaddedFilemode
	fsmonitor.allowRemote
	fsmonitor.socketDir
	gc.<pattern>.reflogExpire
	gc.<pattern>.reflogExpireUnreachable
	gc.aggressiveDepth
	gc.aggressiveWindow
	gc.auto
	gc.autoDetach
	gc.autoPackLimit
	gc.bigPackThreshold
	gc.cruftPacks
	gc.logExpiry
	gc.packRefs
	gc.pruneExpire
	gc.reflogExpire
	gc.reflogExpireUnreachable
	gc.rerereResolved
	gc.rerereUnresolved
	gc.worktreePruneExpire
	gc.writeCommitGraph
	gitcvs.allBinary
	gitcvs.commitMsgAnnotation
	gitcvs.dbDriver
	gitcvs.dbName
	gitcvs.dbPass
	gitcvs.dbTableNamePrefix
	gitcvs.dbUser
	gitcvs.enabled
	gitcvs.logFile
	gitcvs.usecrlfattr
	gitweb.avatar
	gitweb.blame
	gitweb.category
	gitweb.description
	gitweb.grep
	gitweb.highlight
	gitweb.owner
	gitweb.patches
	gitweb.pickaxe
	gitweb.remote_heads
	gitweb.showSizes
	gitweb.snapshot
	gitweb.url
	gpg.<format>.program
	gpg.format
	gpg.minTrustLevel
	gpg.ssh.allowedSignersFile
	gpg.ssh.defaultKeyCommand
	gpg.ssh.revocationFile
	grep.column
	grep.extendedRegexp
	grep.fallbackToNoIndex
	grep.fullName
	grep.lineNumber
	grep.patternType
	grep.threads
	gui.blamehistoryctx
	gui.commitMsgWidth
	gui.copyBlameThreshold
	gui.diffContext
	gui.displayUntracked
	gui.encoding
	gui.fastCopyBlame
	gui.matchTrackingBranch
	gui.newBranchTemplate
	gui.pruneDuringFetch
	gui.spellingDictionary
	gui.trustmtime
	guitool.<name>.argPrompt
	guitool.<name>.cmd
	guitool.<name>.confirm
	guitool.<name>.needsFile
	guitool.<name>.noConsole
	guitool.<name>.noRescan
	guitool.<name>.prompt
	guitool.<name>.revPrompt
	guitool.<name>.revUnmerged
	guitool.<name>.title
	help.autoCorrect
	help.browser
	help.format
	help.htmlPath
	http.<url>.*
	http.cookieFile
	http.curloptResolve
	http.delegation
	http.emptyAuth
	http.extraHeader
	http.followRedirects
	http.lowSpeedLimit
	http.lowSpeedTime
	http.maxRequests
	http.minSessions
	http.noEPSV
	http.pinnedPubkey
	http.postBuffer
	http.proxy
	http.proxyAuthMethod
	http.proxySSLCAInfo
	http.proxySSLCert
	http.proxySSLCertPasswordProtected
	http.proxySSLKey
	http.saveCookies
	http.schannelCheckRevoke
	http.schannelUseSSLCAInfo
	http.sslAutoClientCert
	http.sslCAPath
	http.sslCert
	http.sslCertPasswordProtected
	http.sslCipherList
	http.sslKey
	http.sslTry
	http.sslVerify
	http.sslVersion
	http.userAgent
	http.version
	i18n.logOutputEncoding
	imap.authMethod
	imap.folder
	imap.host
	imap.pass
	imap.port
	imap.preformattedHTML
	imap.sslverify
	imap.tunnel
	imap.user
	include.path
	includeIf.<condition>.path
	index.recordEndOfIndexEntries
	index.recordOffsetTable
	index.skipHash
	index.sparse
	index.threads
	index.version
	init.templateDir
	instaweb.browser
	instaweb.httpd
	instaweb.local
	instaweb.modulePath
	instaweb.port
	interactive.diffFilter
	interactive.singleKey
	log.abbrevCommit
	log.date
	log.decorate
	log.diffMerges
	log.excludeDecoration
	log.follow
	log.graphColors
	log.initialDecorationSet
	log.mailmap
	log.showRoot
	log.showSignature
	lsrefs.unborn
	mailinfo.scissors
	mailmap.blob
	mailmap.file
	maintenance.<task>.enabled
	maintenance.<task>.schedule
	maintenance.auto
	maintenance.commit-graph.auto
	maintenance.incremental-repack.auto
	maintenance.loose-objects.auto
	maintenance.strategy
	man.<tool>.cmd
	man.<tool>.path
	man.viewer
	merge.<driver>.driver
	merge.<driver>.name
	merge.<driver>.recursive
	merge.autoStash
	merge.branchdesc
	merge.conflictStyle
	merge.defaultToUpstream
	merge.directoryRenames
	merge.ff
	merge.log
	merge.renameLimit
	merge.renames
	merge.renormalize
	merge.stat
	merge.suppressDest
	merge.verbosity
	merge.verifySignatures
	mergetool.<tool>.cmd
	mergetool.<tool>.hideResolved
	mergetool.<tool>.path
	mergetool.<tool>.trustExitCode
	mergetool.guiDefault
	mergetool.hideResolved
	mergetool.keepTemporaries
	mergetool.meld.hasOutput
	mergetool.meld.useAutoMerge
	mergetool.prompt
	mergetool.vimdiff.layout
	mergetool.writeToTemp
	notes.<name>.mergeStrategy
	notes.displayRef
	notes.mergeStrategy
	notes.rewrite.<command>
	notes.rewriteMode
	notes.rewriteRef
	pack.allowPackReuse
	pack.compression
	pack.deltaCacheLimit
	pack.deltaCacheSize
	pack.depth
	pack.indexVersion
	pack.island
	pack.islandCore
	pack.packSizeLimit
	pack.preferBitmapTips
	pack.readReverseIndex
	pack.threads
	pack.useBitmaps
	pack.useSparse
	pack.window
	pack.windowMemory
	pack.writeBitmapHashCache
	pack.writeBitmapLookupTable
	pack.writeReverseIndex
	pager.<cmd>
	pretty.<name>
	protocol.<name>.allow
	protocol.allow
	protocol.version
	pull.ff
	pull.octopus
	pull.twohead
	push.autoSetupRemote
	push.default
	push.followTags
	push.gpgSign
	push.negotiate
	push.pushOption
	push.recurseSubmodules
	push.useBitmaps
	push.useForceIfIncludes
	rebase.abbreviateCommands
	rebase.backend
	rebase.forkPoint
	rebase.instructionFormat
	rebase.missingCommitsCheck
	rebase.rebaseMerges
	rebase.rescheduleFailedExec
	rebase.stat
	receive.advertiseAtomic
	receive.advertisePushOptions
	receive.autogc
	receive.certNonceSeed
	receive.certNonceSlop
	receive.denyCurrentBranch
	receive.denyDeleteCurrent
	receive.denyDeletes
	receive.denyNonFastForwards
	receive.fsck.badDate
	receive.fsck.badDateOverflow
	receive.fsck.badEmail
	receive.fsck.badFilemode
	receive.fsck.badName
	receive.fsck.badObjectSha1
	receive.fsck.badParentSha1
	receive.fsck.badTagName
	receive.fsck.badTimezone
	receive.fsck.badTree
	receive.fsck.badTreeSha1
	receive.fsck.badType
	receive.fsck.duplicateEntries
	receive.fsck.emptyName
	receive.fsck.extraHeaderEntry
	receive.fsck.fullPathname
	receive.fsck.gitattributesBlob
	receive.fsck.gitattributesLarge
	receive.fsck.gitattributesLineLength
	receive.fsck.gitattributesMissing
	receive.fsck.gitattributesSymlink
	receive.fsck.gitignoreSymlink
	receive.fsck.gitmodulesBlob
	receive.fsck.gitmodulesLarge
	receive.fsck.gitmodulesMissing
	receive.fsck.gitmodulesName
	receive.fsck.gitmodulesParse
	receive.fsck.gitmodulesPath
	receive.fsck.gitmodulesSymlink
	receive.fsck.gitmodulesUpdate
	receive.fsck.gitmodulesUrl
	receive.fsck.hasDot
	receive.fsck.hasDotdot
	receive.fsck.hasDotgit
	receive.fsck.mailmapSymlink
	receive.fsck.missingAuthor
	receive.fsck.missingCommitter
	receive.fsck.missingEmail
	receive.fsck.missingNameBeforeEmail
	receive.fsck.missingObject
	receive.fsck.missingSpaceBeforeDate
	receive.fsck.missingSpaceBeforeEmail
	receive.fsck.missingTag
	receive.fsck.missingTagEntry
	receive.fsck.missingTaggerEntry
	receive.fsck.missingTree
	receive.fsck.missingType
	receive.fsck.missingTypeEntry
	receive.fsck.multipleAuthors
	receive.fsck.nulInCommit
	receive.fsck.nulInHeader
	receive.fsck.nullSha1
	receive.fsck.skipList
	receive.fsck.treeNotSorted
	receive.fsck.unknownType
	receive.fsck.unterminatedHeader
	receive.fsck.zeroPaddedDate
	receive.fsck.zeroPaddedFilemode
	receive.fsckObjects
	receive.hideRefs
	receive.keepAlive
	receive.maxInputSize
	receive.procReceiveRefs
	receive.shallowUpdate
	receive.unpackLimit
	receive.updateServerInfo
	remote.<name>.fetch
	remote.<name>.mirror
	remote.<name>.partialclonefilter
	remote.<name>.promisor
	remote.<name>.proxy
	remote.<name>.proxyAuthMethod
	remote.<name>.prune
	remote.<name>.pruneTags
	remote.<name>.push
	remote.<name>.pushurl
	remote.<name>.receivepack
	remote.<name>.skipDefaultUpdate
	remote.<name>.skipFetchAll
	remote.<name>.tagOpt
	remote.<name>.uploadpack
	remote.<name>.url
	remote.<name>.vcs
	remote.pushDefault
	remotes.<group>
	repack.cruftDepth
	repack.cruftThreads
	repack.cruftWindow
	repack.cruftWindowMemory
	repack.packKeptObjects
	repack.updateServerInfo
	repack.useDeltaBaseOffset
	repack.useDeltaIslands
	repack.writeBitmaps
	rerere.autoUpdate
	revert.reference
	safe.bareRepository
	safe.directory
	sendemail.<identity>.*
	sendemail.aliasFileType
	sendemail.aliasesFile
	sendemail.annotate
	sendemail.bcc
	sendemail.cc
	sendemail.ccCmd
	sendemail.chainReplyTo
	sendemail.confirm
	sendemail.envelopeSender
	sendemail.forbidSendmailVariables
	sendemail.from
	sendemail.headerCmd
	sendemail.identity
	sendemail.multiEdit
	sendemail.signedoffbycc
	sendemail.smtpBatchSize
	sendemail.smtpDomain
	sendemail.smtpEncryption
	sendemail.smtpPass
	sendemail.smtpReloginDelay
	sendemail.smtpServer
	sendemail.smtpServerOption
	sendemail.smtpServerPort
	sendemail.smtpUser
	sendemail.smtpsslcertpath
	sendemail.suppressFrom
	sendemail.suppresscc
	sendemail.thread
	sendemail.to
	sendemail.tocmd
	sendemail.transferEncoding
	sendemail.validate
	sendemail.xmailer
	sendpack.sideband
	sequence.editor
	showBranch.default
	sparse.expectFilesOutsideOfPatterns
	splitIndex.maxPercentChange
	splitIndex.sharedIndexExpire
	ssh.variant
	stash.showIncludeUntracked
	stash.showPatch
	stash.showStat
	status.aheadBehind
	status.branch
	status.displayCommentPrefix
	status.relativePaths
	status.renameLimit
	status.renames
	status.short
	status.showStash
	status.showUntrackedFiles
	status.submoduleSummary
	submodule.<name>.active
	submodule.<name>.branch
	submodule.<name>.fetchRecurseSubmodules
	submodule.<name>.ignore
	submodule.<name>.update
	submodule.<name>.url
	submodule.active
	submodule.alternateErrorStrategy
	submodule.alternateLocation
	submodule.fetchJobs
	submodule.propagateBranches
	submodule.recurse
	tag.forceSignAnnotated
	tag.sort
	tar.umask
	trace2.configParams
	trace2.destinationDebug
	trace2.envVars
	trace2.eventBrief
	trace2.eventNesting
	trace2.eventTarget
	trace2.maxFiles
	trace2.normalBrief
	trace2.normalTarget
	trace2.perfBrief
	trace2.perfTarget
	transfer.advertiseSID
	transfer.bundleURI
	transfer.credentialsInUrl
	transfer.fsckObjects
	transfer.hideRefs
	transfer.unpackLimit
	uploadarchive.allowUnreachable
	uploadpack.allowAnySHA1InWant
	uploadpack.allowFilter
	uploadpack.allowReachableSHA1InWant
	uploadpack.allowRefInWant
	uploadpack.allowTipSHA1InWant
	uploadpack.hideRefs
	uploadpack.keepAlive
	uploadpack.packObjectsHook
	uploadpackfilter.<filter>.allow
	uploadpackfilter.allow
	uploadpackfilter.tree.maxDepth
	url.<base>.insteadOf
	url.<base>.pushInsteadOf
	user.useConfigOnly
	versionsort.suffix
	web.browser
	windows.appendAtomically
	worktree.guessRemote
	a.a.C
	$#@@#$%&#@
	1234
	~x.Y
	  test
Success
	commit.gpgSign
	core.autocrlf
	core.bare
	core.commitGraph
	core.editor
	core.fileMode
	core.fscache
	core.ignoreCase
	core.logAllRefUpdates
	core.preloadIndex
	core.repositoryFormatVersion
	core.safecrlf
	core.symlinks
	credential.helper
	diff.guitool
	diff.tool
	difftool.prompt
	fetch.prune
	gpg.program
	http.sslBackend
	http.sslCAInfo
	i18n.commitEncoding
	init.defaultBranch
	merge.guitool
	merge.tool
	mergetool.keepBackup
	pull.rebase
	rebase.autoSquash
	rebase.autoStash
	rebase.updateRefs
	rerere.enabled
	tag.gpgSign
	user.email
	user.name
	user.signingKey
NULL
	
	  
	
	''
	'  '
	''

image

image
image

   at GitCommands.ExecutableExtensions.<GetOutputAsync>d__5.MoveNext() in C:\git\gitextensions\GitCommands\Git\ExecutableExtensions.cs:line 129
   at Microsoft.VisualStudio.Threading.JoinableTask.CompleteOnCurrentThread()
   at Microsoft.VisualStudio.Threading.JoinableTask`1.CompleteOnCurrentThread()
   at GitCommands.ExecutableExtensions.GetOutput(IExecutable executable, ArgumentString arguments, Byte[] input, Encoding outputEncoding, CommandCache cache, Boolean stripAnsiEscapeCodes) in C:\git\gitextensions\GitCommands\Git\ExecutableExtensions.cs:line 43
   at GitCommands.GitModule.GetEffectiveGitSetting(String setting, Boolean cache) in C:\git\gitextensions\GitCommands\Git\GitModule.cs:line 2476
   at GitUITests.GitUICommandsTests.RunCommandTests.<ValidateAllConfigOptionsWork>b__49_0(String cfg) in C:\git\gitextensions\IntegrationTests\UI.IntegrationTests\GitUICommands\RunCommandTests.cs:line 374
   at System.Linq.Enumerable.SelectArrayIterator`2.ToArray()
   at System.Linq.Enumerable.ToArray[TSource](IEnumerable`1 source)
   at GitUITests.GitUICommandsTests.RunCommandTests.ValidateAllConfigOptionsWork() in C:\git\gitextensions\IntegrationTests\UI.IntegrationTests\GitUICommands\RunCommandTests.cs:line 374

diff --git a/IntegrationTests/UI.IntegrationTests/GitUICommands/RunCommandTests.cs b/IntegrationTests/UI.IntegrationTests/GitUICommands/RunCommandTests.cs
index 701e9affa..0002a71c3 100644
--- a/IntegrationTests/UI.IntegrationTests/GitUICommands/RunCommandTests.cs
+++ b/IntegrationTests/UI.IntegrationTests/GitUICommands/RunCommandTests.cs
@@ -4,6 +4,7 @@
 using GitCommands;
 using GitExtensions.UITests;
 using GitExtensions.UITests.CommandsDialogs;
+using GitExtUtils;
 using GitUI;
 using GitUI.CommandsDialogs;
 using GitUIPluginInterfaces;
@@ -17,7 +18,7 @@ public sealed class RunCommandTests
     {
         // Created once for the fixture
         private ReferenceRepository _referenceRepository;
-
+        private string[] _allConfigs;
         // Created once for each test
         private GitUICommands _commands;
 
@@ -35,6 +36,10 @@ public void SetUp()
                 _referenceRepository.Module.GitExecutable.RunCommand("config --local diff.guitool cmd").Should().BeTrue();
                 _referenceRepository.Module.GitExecutable.RunCommand("config --local merge.guitool cmd").Should().BeTrue();
 
+                ExecutionResult getAllComfigsResult = _referenceRepository.Module.GitExecutable.Execute(new GitArgumentBuilder("help", gitOptions: (ArgumentString)"--no-pager") { "--config" });
+                _allConfigs = getAllComfigsResult.StandardOutput.Split('\n').Where(cfg => !string.IsNullOrWhiteSpace(cfg) && !cfg.Contains("git help config"))
+                    .Concat(new string[] { "a.a.C", "$#@@#$%&#@", "1234", "~x.Y", "", "  ", "  test", null })
+                    .ToArray();
                 AppSettings.UseConsoleEmulatorForCommands = false;
                 AppSettings.CloseProcessDialog = true;
                 AppSettings.UseBrowseForFileHistory.Value = false;
@@ -361,6 +366,21 @@ public void RunCommandBasedOnArgument_unsupported(string command)
                 });
         }
 
+        [Test]
+        [Description("Makes sure getting a git config value does not raise an exception "
+            + "and even if config value is not set it will just return a blank value")]
+        public void ValidateAllConfigOptionsWork()
+        {
+            var settings = _allConfigs.Select(cfg => new { Setting = _referenceRepository.Module.GetEffectiveGitSetting(cfg, false), ConfigName = cfg }).ToArray();
+            settings.Should().NotContainNulls();
+            settings.Select(s => s.Setting).Should().NotContainNulls();
+
+            foreach (var setting in settings)
+            {
+                Console.WriteLine($"{setting.ConfigName}:{setting.Setting}");
+            }
+        }
+
         private static void ClickButton(Form form, string buttonName)
             => form.FindDescendantOfType<Button>(button => button.Name == buttonName).PerformClick();
 

GetEffectiveGitSetting should not assume config is set. it should mimic the way git config works and provide the value like git config does where unset.

@gerhardol
Copy link
Member

If the caller is OK with exit code 1 is OK, then it may be OK to have a specific access function like GetEffectiveGitSettingOrDefault() - or an argument.
TryGet is an option, but it may be easier to skip the OH with exposing the special handling.
The Git-result parser has the responsibility to handle the error codes.
All Git commands hanles exitCode!=0 as error or ignore, the only command right now with specific checks for exitCode==1 is mergetool in FormResolveConflicts.cs

The other error codes still seem as errors to me - or they need special handling too.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
🚧 status: in progress Issues which have associated PRs
Projects
None yet
Development

Successfully merging a pull request may close this issue.

4 participants