<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[Naveed Ausaf's blog]]></title><description><![CDATA[Naveed Ausaf's blog]]></description><link>https://www.naveedausaf.com</link><generator>RSS for Node</generator><lastBuildDate>Mon, 07 Sep 2026 17:33:48 GMT</lastBuildDate><atom:link href="https://www.naveedausaf.com/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><item><title><![CDATA[How to get XUnit v3 to work with Test Explorer in VS Code]]></title><description><![CDATA[I recently upgraded some xUnit v2 test projects to v3 because I needed two awesome features that v3 introduced:

Assembly Fixtures: These allow a fixture instance to be shared between all tests in mul]]></description><link>https://www.naveedausaf.com/how-to-get-xunit-v3-to-work-with-test-explorer-in-vs-code</link><guid isPermaLink="true">https://www.naveedausaf.com/how-to-get-xunit-v3-to-work-with-test-explorer-in-vs-code</guid><category><![CDATA[Testing]]></category><category><![CDATA[xunit]]></category><category><![CDATA[C#]]></category><category><![CDATA[VS Code]]></category><category><![CDATA[.net core]]></category><dc:creator><![CDATA[Naveed Ausaf]]></dc:creator><pubDate>Mon, 01 Jun 2026 16:50:32 GMT</pubDate><enclosure url="https://cdn.hashnode.com/uploads/covers/66df9b90154f69c2562f9372/4a8f931f-1184-44a8-885e-e328a922f0f3.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>I recently upgraded some xUnit v2 test projects to v3 because I needed two awesome features that v3 introduced:</p>
<ul>
<li><p><a href="https://xunit.net/docs/shared-context#assembly-fixture">Assembly Fixtures</a>: These allow a fixture instance to be shared between all tests in multiple classes, rather than between tests in a single class as <code>IClassFixture</code> had allowed up until v2.</p>
</li>
<li><p>Automatically serialization of test case objects in <code>TheoryData</code> classes. This means that this unbelievable clunk:</p>
<pre><code class="language-csharp"> public class TestCase_ValidProduct : IXunitSerializable
 {
    public required string TestCaseName { get; set; }
    public required CreateProductArgs NewProduct { get; set; }

    public void Deserialize(IXunitSerializationInfo info)
    {
        XUnitSerializationHelper.Deserialize(this, info);
    }

    public void Serialize(IXunitSerializationInfo info)
    {
        XUnitSerializationHelper.Serialize(this, info);
    }
 }
</code></pre>
<p>reduces to:</p>
<pre><code class="language-csharp">public class TestCase_ValidProduct : IXunitSerializable
{
    public required string TestCaseName { get; set; }
    public required CreateProductArgs NewProduct { get; set; }
}
</code></pre>
</li>
</ul>
<p>The price of this upgrade was that Test Explorer in VS Code either couldn't discover tests, or would show them as "Skipped" and refuse to run them:</p>
<img src="https://dev-to-uploads.s3.amazonaws.com/uploads/articles/jdlxuh5knw18vvoa74sq.png" alt="Image description" style="display:block;margin:0 auto" />

<p>Exactly the same thing happens when I scaffold a new XUnit 3 project using <code>dotnet new xunit3</code>.</p>
<p>These problems seem to stem from the fact that xUnit v3 uses the new <a href="https://learn.microsoft.com/en-us/dotnet/core/testing/microsoft-testing-platform-intro">Microsoft Test Platform (MTP)</a> which is a replacement for the older <a href="https://github.com/microsoft/vstest">VS Test</a> platform, but C# Dev Kit extension in VS Code - which is <a href="https://code.visualstudio.com/docs/csharp/testing">responsible for discovering tests in a C# test project</a> and providing these to the Test Explorer window - does not work properly with MTP (yet!).</p>
<p>It is worth mentioning that both VS Test and the newer MTP are <em>test execution platforms</em> - they are agnostic to the test harness used in test projects and works with any test harness e.g. MS Test, xUnit and NUnit.</p>
<p>I fixed the issues by making the test projects use the older VS Test platform instead that C# Dev Kit <em>does</em> work with.</p>
<p>These are the <strong>steps that you can take</strong> to make an xUnit v3 project - whether upgraded from v2 or newly scaffolded - work in Test Explorer in VS Code:</p>
<ul>
<li><p>Delete <code>&lt;PackageReference&gt;</code> to <code>xunit.v3.mtp-v2</code> or just comment it out in the <code>.csproj</code> of your test project:</p>
<pre><code class="language-xml">&lt;!--&lt;PackageReference Include="xunit.v3.mtp-v2" Version="3.2.2" /&gt;--&gt;
</code></pre>
</li>
<li><p>Then add the following references:</p>
<pre><code class="language-bash">dotnet add package xunit.v3.mtp-off
dotnet add package Microsoft.NET.Test.Sdk
dotnet add package xunit.runner.visualstudio
</code></pre>
<p><code>xunit.v3.mtp-off</code> is a version of xUnit v3 package that has <a href="https://xunit.net/docs/getting-started/v3/microsoft-testing-platform#choosing-the-microsoft-testing-platform-version">MTP support disabled</a> and is a replacement for <code>xunit.v3.mtp-v2</code> that I deleted above.</p>
<p><a href="https://www.nuget.org/packages/microsoft.net.test.sdk/#readme-body-tab"><code>Microsoft.NET.Test.Sdk</code></a> provides targets and properties for building .NET test projects, regardless of the test harness - xUnit, NUnit, MS Test etc. - that they use. It is required for integrating the project with the VS Test platform.</p>
<p><a href="https://www.nuget.org/packages/xunit.runner.visualstudio"><code>xunit.runner.visualstudio</code></a> is the xUnit-specific adapter for VS Test</p>
</li>
</ul>
<p>Then reload the window (bring up the Command Palette using shortcut key <code>Ctrl+Shift+P</code> or <code>F12</code>, then type <strong>Reload Window</strong> and press Enter), and press <strong>Refresh Tests</strong> button in <strong>Test Explorer</strong>:</p>
<img src="https://dev-to-uploads.s3.amazonaws.com/uploads/articles/9onecapgg3gogm0ttnhq.png" alt="Image description" style="display:block;margin:0 auto" />

<p>You should now be able to see and run your tests.</p>
<p>The slight caveat is that individual TheoryData tests cases in a <code>[Theory]</code> test do not appear as separate tests cases like they used in the Test Explorer with xUnit v2. But I am happy to sacrifice this granularity for being able to use the shiny new features in xUnit v3.</p>
]]></content:encoded></item><item><title><![CDATA[Accept the Official Hack: Build-Time OpenAPI Detection in .NET 10 Minimal APIs]]></title><description><![CDATA[It is straightforward to configure a minimal API to produce an OpenAPI document at build time. This runs the API during build, requests the OpenAPI document from it, and saves it to disk.
The slightly]]></description><link>https://www.naveedausaf.com/accept-the-official-hack-build-time-openapi-detection-in-net-10-minimal-apis</link><guid isPermaLink="true">https://www.naveedausaf.com/accept-the-official-hack-build-time-openapi-detection-in-net-10-minimal-apis</guid><category><![CDATA[OpenApi]]></category><category><![CDATA[dotnet]]></category><category><![CDATA[asp.net core]]></category><category><![CDATA[api]]></category><dc:creator><![CDATA[Naveed Ausaf]]></dc:creator><pubDate>Sat, 30 May 2026 09:33:13 GMT</pubDate><enclosure url="https://cdn.hashnode.com/uploads/covers/66df9b90154f69c2562f9372/86244de9-8019-4ea1-99fb-b9c920a2c333.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>It is straightforward to configure a minimal API to <a href="https://learn.microsoft.com/en-us/aspnet/core/fundamentals/openapi/aspnetcore-openapi?view=aspnetcore-10.0&amp;tabs=net-cli%2Cnetcore-cli#generate-openapi-documents-at-build-time">produce an OpenAPI document at build time</a>. This runs the API during build, requests the OpenAPI document from it, and saves it to disk.</p>
<p>The slightly trickier part is to put checks in <code>Program.cs</code> to exclude any startup code that cannot run at build time. This is typically done because configuration key/value pairs are not available at that time. For example:</p>
<pre><code class="language-csharp">if (!isBuildTime)
{
    connString = builder.Configuration.GetConnectionString("AppDB") ?? 
    throw new InvalidOperationException("Connection string 'AppDB' is not configured.");

    builder.Services.AddDbContext&lt;AppDbContext&gt;(
        options =&gt;
        {
            options.UseNpgsql(connString);
        }
    );
}
</code></pre>
<p>The question is how to deduce that the API has been launched at build time, i.e. <code>isBuildTime</code> should be true?</p>
<p>The <a href="https://learn.microsoft.com/en-us/aspnet/core/fundamentals/openapi/aspnetcore-openapi?view=aspnetcore-10.0&amp;tabs=net-cli%2Cnetcore-cli#customize-runtime-behavior-during-build-time-document-generation">official way</a> of doing this is to check that the assembly that invoked the API is <code>"GetDocument.Insider"</code>:</p>
<pre><code class="language-csharp">var isBuildTime = 
	Assembly.GetEntryAssembly()?.GetName().Name == "GetDocument.Insider";
</code></pre>
<p><a href="https://github.com/dotnet/aspnetcore/blob/main/src/Tools/GetDocumentInsider/README.md"><code>GetDocument.Insider.dll</code></a> is the command line tool that automatically runs during build of the API if the <code>.csproj</code> includes the following reference:</p>
<pre><code class="language-xml">&lt;PackageReference Include="Microsoft.Extensions.ApiDescription.Server"
	 Version="10.0.7"&gt;
      ...
&lt;/PackageReference&gt;
</code></pre>
<p>This package is a shim. It only provides build targets and props and hooks into the build of the API to <a href="https://github.com/dotnet/dotnet/blob/551f471e3c3df1828061c6faaa9dc9161fdbb154/src/aspnetcore/src/Tools/Extensions.ApiDescription.Server/src/build/Microsoft.Extensions.ApiDescription.Server.targets#L55-L69">run the command-line tool <code>dotnet-getdocument</code></a>. This tool in turn <a href="https://github.com/dotnet/aspnetcore/blob/main/src/Tools/dotnet-getdocument/README.md">runs the command line tool <code>GetDocument.Insider</code></a> that we check for.</p>
<p>This is a pretty convoluted sequence:</p>
<ol>
<li><p><code>Microsoft.Extensions.ApiDescription.Server</code> provides targets that run during build of the API.</p>
</li>
<li><p>One of those targets runs the command line tool <code>dotnet-getdocument</code></p>
</li>
<li><p>That in turn runs the command line tool <code>GetDocument.Insider</code></p>
</li>
<li><p>That in turn runs the API and fetches the <code>/openapi/v1/json</code> (or other configured endpoint) to get the OpenAPI document and saves it to disk.</p>
</li>
</ol>
<p>Checking in Program.cs if the API was invoked by the assembly <code>GetDocument.Insider.dll</code> to determine if it is running during build bothers me. It is a <em>hack</em>, and an uncomfortable one, for two reasons.</p>
<p>First, the name of the tool could change. If the sequence above gets cleaned up or modified in the future, the assembly <code>GetDocument.Insider</code> might vanish. This means we would need to change the check we do to compute if the API is running at build time for OpenAPI document generation.</p>
<p>Second, there is a standard, time-worn way for checking in ASP.NET if <code>Program.cs</code> is running in a specific environment and executing code conditionally based on that: <a href="https://learn.microsoft.com/en-us/aspnet/core/fundamentals/environments?view=aspnetcore-10.0">runtime environments</a>.</p>
<p>We could define a custom environment name, say <code>BuildTime</code>, that sits alongside the predefined environments <code>Production</code>, <code>Development</code> and <code>Staging</code>, and check for it in <code>Program.cs</code>:</p>
<pre><code class="language-plaintext">isBuildTime = builder.Environment.IsEnvironment("BuildTime")
</code></pre>
<p>This is the canonical solution to the problem of determining if the API was launched at build time. The environment name is not going to change either - unlike the assembly name used in the official solution - because this is a custom environment name that we have handpicked for our build.</p>
<p>Unfortunately, there is no official way of declaring the environment name under which the API should be launched during build. Nor would you have any success with setting the environment variables <code>ASPNETCORE_ENVIRONMENT</code> or <code>DOTNET_ENVIRONMENT</code> on the command line before building the API. In other words, this would not work:</p>
<pre><code class="language-bash">export ASPNETCORE_ENVIRONMENT=BuildTime
dotnet build
</code></pre>
<p>The environment simply doesn't get picked up and passed to the API by the tooling that generates the OpenAPI document at build time.</p>
<p>However, I did succeed in setting the environment name so that it gets picked up by the API when it runs during build.</p>
<p>I show my solution in the next section. But my conclusion is that, even though it sounds like the right thing to do, actually doing it is even more fragile and <em>hacky</em> than the official solution.</p>
<p>So, until .NET provides an official way of providing a custom environment name to an API during build-time OpenAPI document generation - and there is an <a href="https://github.com/dotnet/aspnetcore/issues/54698">open issue in <code>dotnet/aspnetcore</code> repo</a> requesting exactly this feature - I would just (grit my teeth and) use the official solution:</p>
<pre><code class="language-csharp">var isBuildTime = 
	Assembly.GetEntryAssembly()?.GetName().Name == "GetDocument.Insider";

if (!isBuildTime) { 
	// load services and middlewares that require 
	// configuration key values that are not available at build time
	
}
</code></pre>
<h2>Providing a custom environment to an API for build-time for OpenAPI generation - DON'T DO THIS</h2>
<p>I define a custom .NET environment named <code>BuildTime</code> in my minimal API project.</p>
<p>While you can pass command-line arguments to the <code>dotnet-getdocument</code> tool using property <code>&lt;OpenApiGenerateDocumentsOptions&gt;</code> in the API's <code>csproj</code>:</p>
<pre><code class="language-xml">&lt;OpenApiGenerateDocumentsOptions&gt;--file-name openapi_v1&lt;/OpenApiGenerateDocumentsOptions&gt;
</code></pre>
<p>none of these corresponds to the environment name of the API that would be launched.</p>
<p>Also, you cannot pass <code>--environment Development</code> command line argument for <code>dotnet run</code> (<code>dotnet build</code> does not have an <code>--environment</code> argument):</p>
<pre><code class="language-bash">dotnet run --environment BuildTime
</code></pre>
<p>using the <code>&lt;OpenApiGenerateDocumentsOptions&gt;</code> csproj property.</p>
<p>Nor can you set <code>ASPNETCORE_ENVIRONMENT</code> or <code>DOTNET_ENVIRONMENT</code> environment variable to <code>"BuildTime"</code> in such a way that it would be available to the API that would be launched during build (as explained in the previous section).</p>
<p>So to get the <code>GetDocument.Insider</code> tool to launch the API in environment <code>"BuildTime"</code>, you essentially have to first construct the command line for running the <code>GetDocument.Insider</code> yourself then execute the command with environment variable <code>ASPNETCORE_ENVIRONMENT</code> set to <code>BuildTime</code>. This is done like this:</p>
<ol>
<li><p>add a reference to <code>Microsoft.Extensions.ApiDescription.Server</code> as before using <code>dotnet add package Microsoft.Extensions.ApiDescription.Server</code> which reflects in the <code>csproj</code> like this:</p>
<pre><code class="language-xml">&lt;PackageReference Include="Microsoft.Extensions.ApiDescription.Server" Version="9.0.6"&gt;
  &lt;IncludeAssets&gt;runtime; build; native; contentfiles; analyzers; buildtransitive&lt;/IncludeAssets&gt;
  &lt;PrivateAssets&gt;all&lt;/PrivateAssets&gt;
&lt;/PackageReference&gt;
</code></pre>
</li>
<li><p>But DISABLE automatic OpenAPI document generation on build to via these properties in the csproj:</p>
<pre><code class="language-xml">&lt;PropertyGroup&gt;
    &lt;OpenApiGenerateDocuments&gt;false&lt;/OpenApiGenerateDocuments&gt;     &lt;OpenApiGenerateDocumentsOnBuild&gt;false&lt;/OpenApiGenerateDocumentsOnBuild&gt;   
&lt;/PropertyGroup&gt;
</code></pre>
<p>Also REMOVE the following properties we added originally. They are not going to be used any more:</p>
<pre><code class="language-xml">&lt;PropertyGroup&gt;
&lt;OpenApiDocumentsDirectory&gt;.&lt;/OpenApiDocumentsDirectory&gt;
&lt;OpenApiGenerateDocumentsOptions&gt;--file-name openapi&lt;/OpenApiGenerateDocumentsOptions&gt;
</code></pre>
</li>
</ol>
<p>```</p>
<ol>
<li><p>Construct the command line for running <code>GetDocument.Insider</code> tool yourself, then execute this command line, by putting these targets in your <code>csproj</code>:</p>
<pre><code class="language-xml">&lt;Target Name="ResolveApiDescriptionPackage"&gt;
    &lt;ItemGroup&gt;
      &lt;_ApiDescriptionPackage Include="@(PackageReference)" Condition="'%(Identity)' == 'Microsoft.Extensions.ApiDescription.Server'" /&gt;
    &lt;/ItemGroup&gt;
  &lt;/Target&gt;
&lt;Target Name="GenerateOpenApiDocumentsAfterBuild" DependsOnTargets="ResolveReferences;ResolveApiDescriptionPackage" AfterTargets="Build"&gt;
    &lt;PropertyGroup&gt;
      &lt;_DetectedApiVersion&gt;%(_ApiDescriptionPackage.Version)&lt;/_DetectedApiVersion&gt;
      &lt;_DotNetGetDocumentCommand&gt;dotnet "\((NuGetPackageRoot)microsoft.extensions.apidescription.server/\)(_DetectedApiVersion)/tools/dotnet-getdocument.dll" --assembly "\((TargetPath)" --file-list "\)(MSBuildProjectDirectory)/obj/cloudcartapi.OpenApiFiles.cache" --framework "\((TargetFrameworkIdentifier),Version=\)(TargetFrameworkVersion)" --output "\((MSBuildProjectDirectory)" --project "\)(MSBuildProjectFullPath)" --assets-file "\((ProjectAssetsFile)" --platform "\)(Platform)" --file-name openapi_v1&lt;/_DotNetGetDocumentCommand&gt;
    &lt;/PropertyGroup&gt;
    &lt;Exec Command="$(_DotNetGetDocumentCommand)" EnvironmentVariables="ASPNETCORE_ENVIRONMENT=BuildTime;DOTNET_ENVIRONMENT=BuildTime" LogStandardErrorAsError="true" /&gt;
  &lt;/Target&gt;
</code></pre>
</li>
<li><p>Finally, for robustness, I add this flourish to the <code>csproj</code>:</p>
<pre><code class="language-xml">&lt;!-- 
The command run in GenerateOpenApiDocumentsAfterBuild target above is a little bit brittle and breaks if the major version of the ApiDescription package does not match the major version of .NET set in &lt;TargetFramework&gt; property a the top.

While this is a very unlikely scenario, because the major version of every .NET package should be/would be the same as the major version of .NET framework that the project compiles against, still f the scenario does arise, instead of getting to the point where GenerateOpenApiDocumentsAfterBuild target runs and fails with an unedifying error, in this task we detect the situation early, show an informative error message and stop the build.
--&gt;
  &lt;Target Name="ValidateApiDescriptionVersion" BeforeTargets="Build" DependsOnTargets="ResolveApiDescriptionPackage"&gt;
    &lt;PropertyGroup&gt;
      &lt;_TFMajorVersion&gt;\(([System.Text.RegularExpressions.Regex]::Match('\)(TargetFramework)', 'net(\d+)').Groups[1].Value)&lt;/_TFMajorVersion&gt;
      &lt;_ApiDescriptionVersion&gt;@(_ApiDescriptionPackage-&gt;'%(Version)')&lt;/_ApiDescriptionVersion&gt;
      &lt;_ApiDescriptionMajorVersion&gt;\(([System.Text.RegularExpressions.Regex]::Match('\)(_ApiDescriptionVersion)', '^(\d+)').Groups[1].Value)&lt;/_ApiDescriptionMajorVersion&gt;
    &lt;/PropertyGroup&gt;
    &lt;Error Condition="'\((_TFMajorVersion)' != '\)(_ApiDescriptionMajorVersion)'" Text="Version mismatch: TargetFramework '\((TargetFramework)' has major version \)(_TFMajorVersion) but Microsoft.Extensions.ApiDescription.Server version '\((_ApiDescriptionVersion)' has major version \)(_ApiDescriptionMajorVersion). Update this package to a version whose major version number is same as that of the target framework (specified in TargetFramework property in this csproj file)." /&gt;
  &lt;/Target&gt;
</code></pre>
</li>
</ol>
<p><strong>The real issue isn't that this solution is a bit messy, it is that it is even more fragile</strong>: if in a future version Microsoft change the name of the file <code>dotnet-getdocument.dll</code> or its location within the package <code>microsoft.extensions.apidescription.server</code> or names of any of the numerous arguments we are passing to it, this logic would break.</p>
<p>On the other hand, in the solution given in MS Docs (and excerpted from there in the article above):</p>
<pre><code class="language-csharp">var isBuildTime = Assembly.GetEntryAssembly()?.GetName().Name == "GetDocument.Insider";
    
if (!isBuildTime) { 
	// load services and middlewares that DO require configuration key values that are not available at build time
	
}
</code></pre>
<p>if the name of assembly changes we would know about it through the Release Notes of the .NET version in which this happens, the new official solution would again be documented/updated, and we can update our code.</p>
<p>Hence why the official, if fragile-looking, solution for conditionally excluding code in <code>Program.cs</code> that requires configuration data is the one that should be used.</p>
]]></content:encoded></item><item><title><![CDATA[The Three Types of Pull Request Merge in GitHub]]></title><description><![CDATA[When you go to merge a pull request on GitHub, you would see three choices:

The behaviour of each of the merge methods is as follows (assuming for the sake of this post that the target branch of the pull request is main, i.e. you are merging into ma...]]></description><link>https://www.naveedausaf.com/types-of-merges-in-a-github-pull-request</link><guid isPermaLink="true">https://www.naveedausaf.com/types-of-merges-in-a-github-pull-request</guid><category><![CDATA[Git]]></category><category><![CDATA[GitHub]]></category><dc:creator><![CDATA[Naveed Ausaf]]></dc:creator><pubDate>Sun, 22 Sep 2024 02:09:53 GMT</pubDate><content:encoded><![CDATA[<p>When you go to merge a pull request on GitHub, you would see three choices:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1726970585142/6c4cfc29-84df-4512-bb23-926699b629ca.png" alt class="image--center mx-auto" /></p>
<p>The behaviour of each of the merge methods is as follows (assuming for the sake of this post that the target branch of the pull request is <code>main</code>, i.e. you are merging into <code>main</code>):</p>
<p><strong>Merge commits</strong> keep all of the commits in the pull request’s source branch and simply add a single new merge commit that points back both to the last commit in the source banch and to the last commit in <code>main</code>.</p>
<p>In the commit graph shown below, the tip of <code>main</code> is the merge commit.</p>
<p>The commit message of the merge commit defaults to the combined pull request and description (topmost comment in the pull request) if this is what you have selected under <strong>Allow merge commits</strong> in Settings:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1726938037341/f4a8cf7b-20e9-4610-bcf5-44aa571be3d4.png" alt class="image--center mx-auto" /></p>
<p><strong>Squash merging</strong> does not carry over source branch’s commits into <code>main</code>. Instead it squashes all changes in the source branch commits and creates a single commit from these and places it in front of the last commit in <code>main</code>.</p>
<p>In the commit graph below, the tip of <code>main</code> is the squash commit. As you can see, unlike a merge commit, it doesn’t point back to the branch <code>add-hello-world</code> that was merged. Instead it squashes the two commits on that branch ahead of <code>main</code> into a single commit. This becomes the new tip of <code>main</code>.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1726961169930/69cd017f-a9c2-4676-8abe-938b1855b51a.png" alt class="image--center mx-auto" /></p>
<p>Again, the message of this commit is the combined pull request title and description if this is what you have selected under Pull Requests in Settings:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1726939494453/97d3a7da-5b9e-4624-8fe7-8262e86a62e8.png" alt class="image--center mx-auto" /></p>
<p><strong>Rebase merging</strong> rebases the source branch onto main (without altering the source branch) and places the new rebased commits in front of <code>main</code>. Hence there is one new commit on <code>main</code> for every commit in the source branch that is ahead of the original tip of <code>main</code>. The last rebased commit becomes the new tip of <code>main</code>.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1726967177618/de48001e-2869-43d7-9aff-c8320312f2a8.png" alt class="image--center mx-auto" /></p>
<p>Each rebased commit retains the commit message of the original commit. Therefore the pull request title or description do not get used in any commit message and there is also no option to use them under <strong>Allow rebase merging</strong> in <strong>Settings</strong>.</p>
<h2 id="heading-my-personal-preference-squash-merge">My personal preference: Squash Merge</h2>
<p>I personally prefer the Squash merge method and disallow the other two because:</p>
<ul>
<li><p>I prefer to keep my <code>main</code> linear as it’s much easier to understand (e.g. to <code>git bisect</code>) than the sort of non-linearhistory yo uget when you have merge commits. Therefore I select option <strong>Require Linear History</strong> in <strong>Branch protection rule</strong> for <code>main</code> (see step XX above).<br />  This precludes the use of merge commits: the merge commit options would not be available on a pull request if you have the require Linear History option checked in the branch protection rule for the target branch (<code>main</code> in this case).</p>
</li>
<li><p>Of the remaining two available merge methods - Squash merge and Rebase merge - I prefer squashing. I like to merge smaller pull request frequently rather than attempt to merge several days or weeks of work on the feature branch into <code>main</code>. For smaller merges, I find that having a single meaningful squash commit on <code>main</code> works a lot better than having lot of tiny commits on <code>main</code> that were obtained by rebasing lots commits on feature branch onto <code>main</code>.</p>
</li>
</ul>
<p>Thus with squash merging, I get a linear <code>main</code> on which individual commits are chunky and meaning rather than a <code>main</code> that is littered with lots of tiny commits.</p>
]]></content:encoded></item><item><title><![CDATA[LF vs CRLF - Configure Git and VS Code to use Unix line endings]]></title><description><![CDATA[Set LF as end of line character in VS Code and Git
To set the default for line endings in VS Code to LF:

go to Command Palette (Ctrl + Shift + P)

type Settings. Choose Preferences: Open Settings (UI) from the list of options that are displayed

In ...]]></description><link>https://www.naveedausaf.com/lf-vs-crlf-configure-git-and-vs-code-to-use-unix-line-endings</link><guid isPermaLink="true">https://www.naveedausaf.com/lf-vs-crlf-configure-git-and-vs-code-to-use-unix-line-endings</guid><category><![CDATA[Git]]></category><category><![CDATA[vscode]]></category><category><![CDATA[Windows]]></category><category><![CDATA[EOL]]></category><category><![CDATA[#LF, CRLF]]></category><dc:creator><![CDATA[Naveed Ausaf]]></dc:creator><pubDate>Mon, 16 Sep 2024 11:39:06 GMT</pubDate><content:encoded><![CDATA[<h2 id="heading-set-lf-as-end-of-line-character-in-vs-code-and-git">Set LF as end of line character in VS Code and Git</h2>
<p><strong>To set the default for line endings in VS Code to LF:</strong></p>
<ul>
<li><p>go to Command Palette (<strong>Ctrl + Shift + P</strong>)</p>
</li>
<li><p>type Settings. Choose <strong>Preferences: Open Settings (UI)</strong> from the list of options that are displayed</p>
</li>
<li><p>In the <strong>Search settings</strong> textbox on the <strong>Settings tab</strong>, type <strong>eol</strong> This would bring up the end of line setting:</p>
<p>  <img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1726824655833/2465aaf5-4f73-4fb6-bec7-a4b51f8b4a7d.png" alt class="image--center mx-auto" /></p>
</li>
<li><p>From the dropdown, select <code>\n</code> (as shown above). This would default VS Code to always use LF line endings in new files.</p>
</li>
<li><p>Close <strong>Settings</strong> tab.</p>
</li>
</ul>
<p>Every new file created in VS Code on Windows would now use LF as the end of line character.</p>
<p><strong>To configure Git on Windows to use LF line endings when committing staged files or when fetching from a remote repo:</strong></p>
<ul>
<li><p>If it does not already exist, create a <code>.gitattributes</code> file in root of the repo's working directory.</p>
</li>
<li><p>Add the following to the <code>.gitattributes</code> file:</p>
<pre><code class="lang-javascript">  * text=auto eol=lf
</code></pre>
</li>
<li><p>Commit your changes:</p>
<pre><code class="lang-javascript">  git add .
  git commit -m <span class="hljs-string">"Configured .gitattributes with LF line endings"</span>
</code></pre>
</li>
</ul>
<p><strong>The</strong> <code>git add .</code> command and fetch from remote should now happen smoothly without warnings about LF and CRLF.</p>
<h2 id="heading-why-do-this">Why do this</h2>
<p>There are two different character sequences that are used in text files to indicate a line break, i.e. end of the current line and beginning of the next one:</p>
<ul>
<li><p>Text editors (and code editors) on Unix-based operating systems, such as Linux and Mac OS, embed a character known as <strong>Line Feed</strong> (ASCII Code 10, Unicode character code also the same, written as <code>0x000A</code> in hexadecimal) in the text to indicate a line break. The name of this character is usually abbreviated to <strong>LF</strong>.</p>
</li>
<li><p>Text- and code editors on Windows typically embed a sequence of two characters at the location of a line break in text: a <strong>Carriage Return</strong> character (<strong>CR</strong> for short; ASCI code 13, in Unicode it is the same and written in hexadecimal as <code>0x000D</code>) followed by a <strong>Line Feed</strong>. This character sequence is usually referred to as <strong>CRLF</strong>.</p>
</li>
</ul>
<p>Characters/character sequences to indicate a line breaks are also referred to as <strong>End of Line (EOL) characters</strong> or as <strong>Line Endings</strong>.</p>
<p>In programming languages (such as JavaScript, C#) the Line Feed character is escaped as <code>\n</code> whereas Carriage Return Character is escaped as <code>\r</code>. In order to see the effect of LF, open F12 Developer Toolbar in the browser. In this go to Console. Type <code>console.log("first line\nsecond line\nthis is the third line")</code> and press Enter:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1726825209655/4da8aa96-4302-4e98-b950-5c296444ed3d.png" alt class="image--center mx-auto" /></p>
<p>You can see that the LF character (escaped using <code>\n</code> in the line of JavaScript code shown) translates into a line break.</p>
<p>Now type the same line with <code>\r\n</code> in places where there was <code>\n</code>, i.e. type the following line in Console and press Enter:</p>
<pre><code class="lang-javascript"><span class="hljs-built_in">console</span>.log(<span class="hljs-string">"first line\r\nsecond line\r\nthis is the third line"</span>)
</code></pre>
<p>This too displays as three separately lines, i.e. the character sequence CRLF was printed as a line break.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1726825263272/8a66971b-2912-41aa-b776-88a622afb6da.png" alt class="image--center mx-auto" /></p>
<p>Both LF and CRLF work as line break when you print text from a programming language, regardless of the operating system on which the code executes. So the two lines of code given above should work on both Windows and on Linux/Unix/MacOS, in Node.js as well in the browser.</p>
<p><strong>However, when editors and text tools on Unix-based systems load files which were authored in Windows and use CRLF line endings, they can have trouble displaying or processing them</strong>. On the other hand, all modern word processors and code editors on Windows can handle the Unix LF line endings perfectly well.</p>
<p>Since teams members may use different operating systems and since open source projects can accept contributions from developers using a variety of operating systems, <strong>it is best practice to ensure that all text files (including code files) in a Git repo use Unix line endings (i.e. LF, and not CRLF)</strong>.</p>
<p>Code scaffolders such as <code>create-next-app</code> typically generate files with LF line endings, whether they are run on Windows or on other operating systems. If you open a code file generated by such a scaffolder and look in the status bar in your VS Code, you should see <strong>LF</strong>, indicating that the line endings used in the file would be LF.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1726825372966/b49aab82-18fb-4884-9e53-e28ff9e9189b.png" alt class="image--center mx-auto" /></p>
<p>However, if you create a new (empty) file, this would default to CRLF on Windows. <strong>Since we want line endings in all files in a Git repo to be LF, VS Code needs to be configured to use LF for all new files</strong>.</p>
<p><strong>Also, Git on Windows also uses CRLF line ending by default</strong>. What this means is that it would replace all standalone LF characters (those not prefixed with the CR character) with CRLF when committing your changes or when fetching a remote repo. So even if all of the files in the local Git working directory had LF endings, when you run <code>git add.</code>, you would get a warning for every file that was added to the local repo's staging area which says that <strong>LF will be replaced by CRLF the next time Git touches</strong> that file:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1725930986122/b5630966-e8dd-4f47-bb58-eed6eb023ea6.png" alt="Warnings shown by git add command when some of the files being added contain LF but the repo's line ending default is CRLF" /></p>
<p>Therefore, <strong>Git on Windows also needs to be configured to use LF as the EOL character</strong> instead of CRLF.</p>
]]></content:encoded></item></channel></rss>