Skip to content

Conversation

@dahlbyk
Copy link
Member

@dahlbyk dahlbyk commented Aug 25, 2013

Stumbled on this setting tonight...

Enable the reflog. Updates to a ref is logged to the file "$GIT_DIR/logs/", by appending the new and old SHA-1, the date/time and the reason of the update, but only when the file exists. If this configuration variable is set to true, missing "$GIT_DIR/logs/" file is automatically created for branch heads (i.e. under refs/heads/), remote refs (i.e. under refs/remotes/), note refs (i.e. under refs/notes/), and the symbolic ref HEAD.

This information can be used to determine what commit was the tip of a branch "2 days ago".

This value is true by default in a repository that has a working directory associated with it, and false by default in a bare repository.

This isn't ready to merge yet, but I wanted to get it out for comment...

Using ObjectDatabase to create a commit to target with a branch was the first thing I thought of to create a reflog entry in a bare repo...did I overlook something obviously simpler?

  • Fix that CreateBranch() does not write to reflog (or I can open a separate issue... easy enough to fix here and I'd like my test to use it instead of the more obtuse Refs.Add(...)).
  • Test for above
  • Complete implementation of repo.Config.GetValueOrDefault(key, defaultValue)? Seems a common enough pattern (even in our tests) to make simple, but didn't want to waste time on overloads/docs if you disagree.

@nulltoken
Copy link
Member

Using ObjectDatabase to create a commit to target with a branch was the first thing I thought of to create a reflog entry in a bare repo...did I overlook something obviously simpler?

I don't think so.

Complete implementation of repo.Config.GetValue(key, defaultValue)?

Why not. Although I'm not sure this makes the code that easier to read.

For instance, if (!repo.Config.GetValue("core.logAllRefUpdates", !repo.Info.IsBare)) took me some time to parse.

@dahlbyk
Copy link
Member Author

dahlbyk commented Aug 25, 2013

Complete implementation of repo.Config.GetValue(key, defaultValue)?

Why not. Although I'm not sure this makes the code that easier to read.

For instance, if (!repo.Config.GetValue("core.logAllRefUpdates", !repo.Info.IsBare)) took me some time to parse.

Well that's not a ringing endorsement... A few alternatives:

  1. Is a local variable sufficient to make it clearer?

    var logAllRefUpdates = repo.Config.GetValue("core.logAllRefUpdates", !repo.Info.IsBare);
    if (!logAllRefUpdates)
    {
       return;
    }
  2. Calling it GetValueOrDefault to better reveal what last parameter means?

    repo.Config.GetValueOrDefault("core.logAllRefUpdates", !repo.Info.IsBare)
  3. Extension could live on ConfigurationEntry<T>, allowing:

    repo.Config.Get<bool>("core.logAllRefUpdates").GetValueOrDefault(!repo.Info.IsBare)
  4. Instead of a method, add an explicit cast from ConfigurationEntry<T> to T to extract Value or default(T). In this case, it's also useful to support Get<bool?>(key) so we can tell if the entry was missing. This allows:

    (bool?)repo.Config.Get<bool?>("core.logAllRefUpdates") ?? !repo.Info.IsBare

    Not a fan of needing to specify the type twice...

If we stick with 1, 2 or 3, I wonder if we should accept a default value lambda instead (in addition?) since evaluating the default might be expensive.

@nulltoken
Copy link
Member

Calling it GetValueOrDefault to better reveal what last parameter means?

  repo.Config.GetValueOrDefault("core.logAllRefUpdates", !repo.Info.IsBare)

I like this one.

If we stick with 1, 2 or 3, I wonder if we should accept a default value lambda instead (in addition?) since evaluating the default might be expensive.

👍

Is a local variable sufficient to make it clearer?

I think that would help. Thanks for bearing with me ;-)

@nulltoken
Copy link
Member

@dahlbyk Shouldn't it rather be repo.Config.GetValueOrDefault("core.logAllRefUpdates", false)?

It looks like this is set at repo (re)initialization time (see in git.git), not dynamically evaluated. Or am I missing something obvious?

@dahlbyk
Copy link
Member Author

dahlbyk commented Aug 26, 2013

@dahlbyk Shouldn't it rather be repo.Config.GetValueOrDefault("core.logAllRefUpdates", false)?

It looks like this is set at repo (re)initialization time (see in git.git), not dynamically evaluated. Or am I missing something obvious?

I suppose I should have checked on that... yeah, since we can expect repos to have this set, then defaulting to false is sufficient. And I've confirmed libgit2 does set true for bare repos.

@nulltoken
Copy link
Member

And I've confirmed libgit2 does set true for bare repos.

Isn't it the opposite? The config entry core.logAllRefUpdate should be set for non bare repos. At least, this is what this test asserts. Or am I reading it wrong?

@dahlbyk
Copy link
Member Author

dahlbyk commented Aug 26, 2013

Er yeah, the opposite. What I meant to say is, libgit2 does the right thing.

@dahlbyk
Copy link
Member Author

dahlbyk commented Aug 27, 2013

Updated per feedback. Need to write tests for BranchCollection changes, otherwise...?

@nulltoken
Copy link
Member

@dahlbyk It looks like Travis choked on the following

ReflogCollection.cs(100,48): error CS0584: Internal compiler error: Object reference not set to an instance of an object
ReflogCollection.cs(101,17): error CS0023: The `!' operator cannot be applied to operand of type `object'

@dahlbyk
Copy link
Member Author

dahlbyk commented Aug 27, 2013

Internal compiler error: Object reference not set to an instance of an object

Achievement unlocked! Guessing it's an issue with generic type inference..pushing a workaround and will see if I can come up with a minimal repro.

@nulltoken
Copy link
Member

@dahlbyk If you can isolate this in a repro case, maybe would that fit a nice Mono bug report.

/cc @joncham

@nulltoken
Copy link
Member

Need to write tests for BranchCollection changes, otherwise...?

Beside covering with tests the reflog changes when creating and moving branches... Perfect!

@dahlbyk
Copy link
Member Author

dahlbyk commented Aug 29, 2013

Internal compiler error: Object reference not set to an instance of an object

Boom goes the msc stack trace:

System.NullReferenceException: Object reference not set to an instance of an object
  at Mono.CSharp.TypeInference.DoSecondPhase (Mono.CSharp.ResolveContext ec, Mono.CSharp.TypeInferenceContext tic, Mono.CSharp.TypeSpec[] methodParameters, Boolean fixDependent)
  at Mono.CSharp.TypeInference.InferInPhases (Mono.CSharp.ResolveContext ec, Mono.CSharp.TypeInferenceContext tic, Mono.CSharp.AParametersCollection methodParameters)
  at Mono.CSharp.TypeInference.InferMethodArguments (Mono.CSharp.ResolveContext ec, Mono.CSharp.MethodSpec method)
  at Mono.CSharp.OverloadResolver.IsApplicable (Mono.CSharp.ResolveContext ec, Mono.CSharp.Arguments& arguments, Int32 arg_count, Mono.CSharp.MemberSpec& candidate, IParametersMember pm, System.Boolean& params_expanded_form, System.Boolean& dynamicArgument, Mono.CSharp.TypeSpec& returnType)
  at Mono.CSharp.OverloadResolver.ResolveMember[MethodSpec] (Mono.CSharp.ResolveContext rc, Mono.CSharp.Arguments& args)
  at Mono.CSharp.MethodGroupExpr.OverloadResolve (Mono.CSharp.ResolveContext ec, Mono.CSharp.Arguments& args, IErrorHandler cerrors, Restrictions restr)
  at Mono.CSharp.ExtensionMethodGroupExpr.OverloadResolve (Mono.CSharp.ResolveContext ec, Mono.CSharp.Arguments& arguments, IErrorHandler ehandler, Restrictions restr)
  at Mono.CSharp.Invocation.DoResolveOverload (Mono.CSharp.ResolveContext ec)
  at Mono.CSharp.Invocation.DoResolve (Mono.CSharp.ResolveContext ec)
  at Mono.CSharp.Expression.Resolve (Mono.CSharp.ResolveContext ec, ResolveFlags flags)

@nulltoken
Copy link
Member

Fix that CreateBranch() does not write to reflog

Spot on! See this SO question : How to detect what commit a branch has been created from in LibGit2Sharp

@dahlbyk
Copy link
Member Author

dahlbyk commented Aug 30, 2013

That should do it... Any other feedback before commit cleanup?

@dahlbyk
Copy link
Member Author

dahlbyk commented Aug 30, 2013

On second thought, this is probably easier to review after cleanup. What say you?

@nulltoken
Copy link
Member

@dahlbyk That's a very nice job!

Just one nitpick. I'm not a huge fan of allowing a null defaultValueSelector, as this leads to weird to read construct such as the following that may lead the reader to think of null as the default for a bool.

Assert.Equal(false, repo.Config.GetValueOrDefault<bool>("missing.key", null));

How about the following?

---
 LibGit2Sharp.Tests/ConfigurationFixture.cs | 11 ++++-------
 LibGit2Sharp/ConfigurationExtensions.cs    | 13 ++++++++++---
 2 files changed, 14 insertions(+), 10 deletions(-)

diff --git a/LibGit2Sharp.Tests/ConfigurationFixture.cs b/LibGit2Sharp.Tests/ConfigurationFixture.cs
index 8dfb2f7..ad8ca8b 100644
--- a/LibGit2Sharp.Tests/ConfigurationFixture.cs
+++ b/LibGit2Sharp.Tests/ConfigurationFixture.cs
@@ -113,7 +113,6 @@ public void CanReadBooleanValue()
                 Assert.True(repo.Config.GetValueOrDefault<bool>("core.ignorecase"));

                 Assert.Equal(false, repo.Config.GetValueOrDefault<bool>("missing.key"));
-                Assert.Equal(false, repo.Config.GetValueOrDefault<bool>("missing.key", null));
                 Assert.Equal(true, repo.Config.GetValueOrDefault<bool>("missing.key", true));
                 Assert.Equal(true, repo.Config.GetValueOrDefault<bool>("missing.key", () => true));
             }
@@ -129,7 +128,6 @@ public void CanReadIntValue()
                 Assert.Equal(2, repo.Config.GetValueOrDefault<int>("unittests.intsetting", ConfigurationLevel.Local));

                 Assert.Equal(0, repo.Config.GetValueOrDefault<int>("missing.key"));
-                Assert.Equal(0, repo.Config.GetValueOrDefault<int>("missing.key", null));
                 Assert.Equal(4, repo.Config.GetValueOrDefault<int>("missing.key", 4));
                 Assert.Equal(4, repo.Config.GetValueOrDefault<int>("missing.key", () => 4));
             }
@@ -144,7 +142,6 @@ public void CanReadLongValue()
                 Assert.Equal(15234, repo.Config.GetValueOrDefault<long>("unittests.longsetting"));

                 Assert.Equal(0, repo.Config.GetValueOrDefault<long>("missing.key"));
-                Assert.Equal(0, repo.Config.GetValueOrDefault<long>("missing.key", null));
                 Assert.Equal(4, repo.Config.GetValueOrDefault<long>("missing.key", 4));
                 Assert.Equal(4, repo.Config.GetValueOrDefault<long>("missing.key", () => 4));
             }
@@ -164,22 +161,22 @@ public void CanReadStringValue()
                 Assert.Equal("+refs/heads/*:refs/remotes/origin/*", repo.Config.GetValueOrDefault<string>(new[] { "remote", "origin", "fetch" }));

                 Assert.Equal(null, repo.Config.GetValueOrDefault<string>("missing.key"));
-                Assert.Equal(null, repo.Config.GetValueOrDefault<string>("missing.key", default(Func<string>)));
+                Assert.Equal(null, repo.Config.GetValueOrDefault<string>("missing.key", default(string)));
                 Assert.Equal("value", repo.Config.GetValueOrDefault<string>("missing.key", "value"));
                 Assert.Equal("value", repo.Config.GetValueOrDefault<string>("missing.key", () => "value"));

                 Assert.Equal(null, repo.Config.GetValueOrDefault<string>("missing.key", ConfigurationLevel.Local));
-                Assert.Equal(null, repo.Config.GetValueOrDefault<string>("missing.key", ConfigurationLevel.Local, default(Func<string>)));
+                Assert.Equal(null, repo.Config.GetValueOrDefault<string>("missing.key", ConfigurationLevel.Local, default(string)));
                 Assert.Equal("value", repo.Config.GetValueOrDefault<string>("missing.key", ConfigurationLevel.Local, "value"));
                 Assert.Equal("value", repo.Config.GetValueOrDefault<string>("missing.key", ConfigurationLevel.Local, () => "value"));

                 Assert.Equal(null, repo.Config.GetValueOrDefault<string>("missing", "config", "key"));
-                Assert.Equal(null, repo.Config.GetValueOrDefault<string>("missing", "config", "key", default(Func<string>)));
+                Assert.Equal(null, repo.Config.GetValueOrDefault<string>("missing", "config", "key", default(string)));
                 Assert.Equal("value", repo.Config.GetValueOrDefault<string>("missing", "config", "key", "value"));
                 Assert.Equal("value", repo.Config.GetValueOrDefault<string>("missing", "config", "key", () => "value"));

                 Assert.Equal(null, repo.Config.GetValueOrDefault<string>(new[] { "missing", "key" }));
-                Assert.Equal(null, repo.Config.GetValueOrDefault<string>(new[] { "missing", "key" }, default(Func<string>)));
+                Assert.Equal(null, repo.Config.GetValueOrDefault<string>(new[] { "missing", "key" }, default(string)));
                 Assert.Equal("value", repo.Config.GetValueOrDefault<string>(new[] { "missing", "key" }, "value"));
                 Assert.Equal("value", repo.Config.GetValueOrDefault<string>(new[] { "missing", "key" }, () => "value"));
             }
diff --git a/LibGit2Sharp/ConfigurationExtensions.cs b/LibGit2Sharp/ConfigurationExtensions.cs
index efbe948..14fdff3 100644
--- a/LibGit2Sharp/ConfigurationExtensions.cs
+++ b/LibGit2Sharp/ConfigurationExtensions.cs
@@ -1,4 +1,5 @@
 using System;
+using System.Diagnostics;
 using LibGit2Sharp.Core;

 namespace LibGit2Sharp
@@ -139,6 +140,8 @@ public static T GetValueOrDefault<T>(this Configuration config, string firstKeyP
         /// <returns>The configuration value, or a generated default.</returns>
         public static T GetValueOrDefault<T>(this Configuration config, string key, Func<T> defaultValueSelector)
         {
+            Ensure.ArgumentNotNull(defaultValueSelector, "defaultValueSelector");
+
             return ValueOrDefault(config.Get<T>(key), defaultValueSelector);
         }

@@ -155,6 +158,8 @@ public static T GetValueOrDefault<T>(this Configuration config, string key, Func
         /// <returns>The configuration value, or a generated default.</returns>
         public static T GetValueOrDefault<T>(this Configuration config, string key, ConfigurationLevel level, Func<T> defaultValueSelector)
         {
+            Ensure.ArgumentNotNull(defaultValueSelector, "defaultValueSelector");
+
             return ValueOrDefault(config.Get<T>(key, level), defaultValueSelector);
         }

@@ -170,6 +175,8 @@ public static T GetValueOrDefault<T>(this Configuration config, string key, Conf
         /// <returns>The configuration value, or a generated default.</returns>
         public static T GetValueOrDefault<T>(this Configuration config, string[] keyParts, Func<T> defaultValueSelector)
         {
+            Ensure.ArgumentNotNull(defaultValueSelector, "defaultValueSelector");
+
             return ValueOrDefault(config.Get<T>(keyParts), defaultValueSelector);
         }

@@ -187,6 +194,8 @@ public static T GetValueOrDefault<T>(this Configuration config, string[] keyPart
         /// <returns>The configuration value, or a generated default.</returns>
         public static T GetValueOrDefault<T>(this Configuration config, string firstKeyPart, string secondKeyPart, string thirdKeyPart, Func<T> defaultValueSelector)
         {
+            Ensure.ArgumentNotNull(defaultValueSelector, "defaultValueSelector");
+
             return ValueOrDefault(config.Get<T>(firstKeyPart, secondKeyPart, thirdKeyPart), defaultValueSelector);
         }

@@ -198,9 +207,7 @@ private static T ValueOrDefault<T>(ConfigurationEntry<T> value, T defaultValue)
         private static T ValueOrDefault<T>(ConfigurationEntry<T> value, Func<T> defaultValueSelector)
         {
             return value == null
-                       ? defaultValueSelector == null
-                             ? default(T)
-                             : defaultValueSelector()
+                       ? defaultValueSelector()
                        : value.Value;
         }
     }
-- 

@dahlbyk
Copy link
Member Author

dahlbyk commented Aug 30, 2013

How does @fefa364 look?

@nulltoken
Copy link
Member

@dahlbyk Awesome!

Squashing time! 🔨 🕙

@dahlbyk
Copy link
Member Author

dahlbyk commented Aug 30, 2013

Squashed

@nulltoken nulltoken merged commit 726266a into libgit2:vNext Aug 30, 2013
@nulltoken
Copy link
Member

Merged 💖

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants