<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:media="http://search.yahoo.com/mrss/"><channel><title>2025-03 on Funky Si's Blog (Dev)</title><link>https://blog-dev.funkysi1701.com/2025/03/</link><description>Recent content in 2025-03 on Funky Si's Blog (Dev)</description><generator>Hugo -- gohugo.io</generator><language>en-gb</language><managingEditor>funkysi1701@gmail.com (Simon Foster)</managingEditor><webMaster>funkysi1701@gmail.com (Simon Foster)</webMaster><lastBuildDate>Mon, 17 Mar 2025 20:00:00 +0000</lastBuildDate><atom:link href="https://blog-dev.funkysi1701.com/2025/03/index.xml" rel="self" type="application/rss+xml"/><item><title>Automate social posts with AI in .NET</title><link>https://blog-dev.funkysi1701.com/posts/2025/using-ai/</link><author>funkysi1701@gmail.com (funkysi1701)</author><pubDate>Mon, 17 Mar 2025 20:00:00 +0000</pubDate><guid>https://blog-dev.funkysi1701.com/posts/2025/using-ai/</guid><category term="AI">AI</category><category term="OpenAI">OpenAI</category><category term="SemanticKernel">SemanticKernel</category><category term="DotNet">DotNet</category><category term="RSS">RSS</category><category term="SocialMedia">SocialMedia</category><category term="Automation">Automation</category><category term="Programming">Programming</category><category term="Tech">Tech</category><media:content medium="image" type="image/png" url="https://blog-dev.funkysi1701.com/images/social-ai.png"/><description>&lt;p&gt;Automating repetitive tasks is one of the best ways to save time and focus on what matters most. In this blog, I&amp;rsquo;ll show you how I used AI with .NET to automate the creation of social media posts for my blog. By leveraging my blog&amp;rsquo;s RSS feed, OpenAI, and Semantic Kernel, I was able to generate engaging posts effortlessly. Let&amp;rsquo;s dive in!&lt;/p&gt;
&lt;h2 id="using-openai-for-social-media-posts"&gt;Using OpenAI for Social Media Posts&lt;a class="anchor ms-1" href="#using-openai-for-social-media-posts" aria-label="Permalink: Using OpenAI for Social Media Posts"&gt;&lt;i class="fas fa-link" aria-hidden="true"&gt;&lt;/i&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Let&amp;rsquo;s look at a way to use AI for a practical use. For the last few weeks I have been using AI to help me write social media posts to promote my latest blog posts, let&amp;rsquo;s look at how I might automate this using .NET code.&lt;/p&gt;
&lt;p&gt;My blog publishes new posts to a rss feed, I can consume this from .NET code and pass it to AI, and create social media posts from them.&lt;/p&gt;
&lt;p&gt;My blog is written in Hugo and it is already generating an rss feed, however I have made a slight tweak to it. I want the tags from the blog article to be used as hashtags in any social media posts. I am storing the tags in the rss field category.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-xml" data-lang="xml"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;&amp;lt;category&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;term=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;tagname&amp;#34;&lt;/span&gt;&lt;span style="color:#f92672"&gt;&amp;gt;&lt;/span&gt;tagname&lt;span style="color:#f92672"&gt;&amp;lt;/category&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;And here is the section of code that generates my rss feed, the key tags I will be using are title, link, category and description.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-xml" data-lang="xml"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;&amp;lt;item&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;lt;title&amp;gt;&lt;/span&gt;{{ .Title }}&lt;span style="color:#f92672"&gt;&amp;lt;/title&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;lt;link&amp;gt;&lt;/span&gt;{{ .Permalink }}&lt;span style="color:#f92672"&gt;&amp;lt;/link&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;lt;author&amp;gt;&lt;/span&gt;{{ with .Params.author }}{{ . }}{{ else }}{{ with $.Site.Author.name }}{{.}}{{end}}{{end}}&lt;span style="color:#f92672"&gt;&amp;lt;/author&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;lt;pubDate&amp;gt;&lt;/span&gt;{{ .Date.Format &amp;#34;Mon, 02 Jan 2006 15:04:05 -0700&amp;#34; | safeHTML }}&lt;span style="color:#f92672"&gt;&amp;lt;/pubDate&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; {{ with .Site.Author.email }}&lt;span style="color:#f92672"&gt;&amp;lt;author&amp;gt;&lt;/span&gt;{{.}}{{ with $.Site.Author.name }} ({{.}}){{end}}&lt;span style="color:#f92672"&gt;&amp;lt;/author&amp;gt;&lt;/span&gt;{{end}}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;lt;guid&amp;gt;&lt;/span&gt;{{ .Permalink }}&lt;span style="color:#f92672"&gt;&amp;lt;/guid&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; {{- range .Params.tags -}}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;lt;category&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;term=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;{{ replace . &amp;#34;&lt;/span&gt; &lt;span style="color:#960050;background-color:#1e0010"&gt;&amp;#34;&lt;/span&gt; &lt;span style="color:#960050;background-color:#1e0010"&gt;&amp;#34;&amp;#34;&lt;/span&gt; &lt;span style="color:#960050;background-color:#1e0010"&gt;}}&amp;#34;&lt;/span&gt;&lt;span style="color:#f92672"&gt;&amp;gt;&lt;/span&gt;{{ replace . &amp;#34; &amp;#34; &amp;#34;&amp;#34; }}&lt;span style="color:#f92672"&gt;&amp;lt;/category&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; {{ end }}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;lt;description&amp;gt;&lt;/span&gt;{{ .Content | html }}&lt;span style="color:#f92672"&gt;&amp;lt;/description&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;&amp;lt;/item&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;I am using the nuget package &lt;a href="https://github.com/arminreiter/FeedReader/" target="_blank" rel="noopener noreferrer"&gt;CodeHollow.FeedReader&lt;/a&gt;
, this allows me to pass in a URL to my rss feed, and get an object out which contains all the items in the feed. Using a nuget package saves me doing a lot of parsing of XML. The method I am using takes an input string something like &lt;a href="https://www.funkysi1701.com/index.xml" target="_blank" rel="noopener noreferrer"&gt;https://www.funkysi1701.com/index.xml&lt;/a&gt;
, and gives me an object I can query and do things with.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-csharp" data-lang="csharp"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;var&lt;/span&gt; RSSFeed = &lt;span style="color:#66d9ef"&gt;await&lt;/span&gt; FeedReader.ReadAsync(FeedStringValue);
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;RSS feed contains the following properties I want to use: Title, Link, Categories and Description. All are strings, except Categories which is an ICollection&amp;lt;string&amp;gt; This matches what I said I wanted to use from the rss feed.&lt;/p&gt;
&lt;p&gt;I want to pass the description to AI, and then combine the Response from AI with URL of the blog post and a comma separated list of &amp;lsquo;hashtags&amp;rsquo; (the categories in my rss feed).&lt;/p&gt;
&lt;p&gt;Initially I thought about using &lt;a href="https://ollama.com/" target="_blank" rel="noopener noreferrer"&gt;ollama&lt;/a&gt;
, this is an example of a LLM (Large Language Model) that you can install on your laptop, sometimes called a Small Language Model (SLM). However, I found this to be unreliable with my laptop and caused several system wide crashes. I assume this is due to memory usage and the size of the model. LLMs or SLMs do require a lot of system resources so this is not completely surprising. I will revisit this at some point as I would like to learn more about ollama.&lt;/p&gt;
&lt;p&gt;I already have an account on &lt;a href="https://platform.openai.com/" target="_blank" rel="noopener noreferrer"&gt;OpenAI&lt;/a&gt;
and some credit so let&amp;rsquo;s try that instead. I have made a fair few requests using OpenAI and so far it has cost me 11p, I don&amp;rsquo;t think for my usage it is going to break the bank. If I was going to put something on the public internet I may have to reconsider but for my own personal use I think this should be fine.&lt;/p&gt;
&lt;p&gt;Next step, how do I talk to OpenAI from .NET. I know of two approaches, &lt;a href="https://github.com/microsoft/semantic-kernel" target="_blank" rel="noopener noreferrer"&gt;Semantic Kernel&lt;/a&gt;
and the new Microsoft.Extensions.AI libraries introduces in .NET 9 &lt;a href="https://devblogs.microsoft.com/dotnet/introducing-microsoft-extensions-ai-preview/" target="_blank" rel="noopener noreferrer"&gt;https://devblogs.microsoft.com/dotnet/introducing-microsoft-extensions-ai-preview/&lt;/a&gt;
. Semantic Kernel is what I am using here, however I do want to explore the newer libraries at some point.&lt;/p&gt;
&lt;p&gt;Semantic Kernel with OpenAI needs the following Nuget package: Microsoft.SemanticKernel.Connectors.OpenAI&lt;/p&gt;
&lt;p&gt;In my Program.cs I had the following to register and add to dependency injection.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-csharp" data-lang="csharp"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; (builder.Configuration.GetValue&amp;lt;&lt;span style="color:#66d9ef"&gt;string&lt;/span&gt;&amp;gt;(&lt;span style="color:#e6db74"&gt;&amp;#34;OpenAI:Key&amp;#34;&lt;/span&gt;) != &lt;span style="color:#66d9ef"&gt;string&lt;/span&gt;.Empty)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; builder.Services.AddOpenAIChatCompletion(
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; modelId: builder.Configuration.GetValue&amp;lt;&lt;span style="color:#66d9ef"&gt;string&lt;/span&gt;&amp;gt;(&lt;span style="color:#e6db74"&gt;&amp;#34;OpenAI:Model&amp;#34;&lt;/span&gt;) ?? &lt;span style="color:#66d9ef"&gt;string&lt;/span&gt;.Empty,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; apiKey: builder.Configuration.GetValue&amp;lt;&lt;span style="color:#66d9ef"&gt;string&lt;/span&gt;&amp;gt;(&lt;span style="color:#e6db74"&gt;&amp;#34;OpenAI:Key&amp;#34;&lt;/span&gt;) ?? &lt;span style="color:#66d9ef"&gt;string&lt;/span&gt;.Empty
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; );
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;builder.Services.AddTransient((serviceProvider) =&amp;gt; &lt;span style="color:#66d9ef"&gt;new&lt;/span&gt; Kernel(serviceProvider));
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;OpenAI:Key is your API Key, which can be found from the OpenAI site, &lt;a href="https://platform.openai.com/api-keys" target="_blank" rel="noopener noreferrer"&gt;https://platform.openai.com/api-keys&lt;/a&gt;
OpenAI:Model is the model I am using, in my case &amp;ldquo;gpt-4o&amp;rdquo;, I&amp;rsquo;ve added it as a config option as I may find I want to change it.&lt;/p&gt;
&lt;p&gt;Now in the code where I want to call OpenAI I can just inject SemanticKernel and call its async methods. I am a backend developer, so I created a simple API that allows me to call it.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-csharp" data-lang="csharp"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;public&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;class&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;AIHandler&lt;/span&gt;(Kernel kernel)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;public&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;async&lt;/span&gt; Task Handle(&lt;span style="color:#66d9ef"&gt;string&lt;/span&gt; request, CancellationToken cancellationToken)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;var&lt;/span&gt; response = &lt;span style="color:#66d9ef"&gt;await&lt;/span&gt; kernel.InvokePromptAsync(request, cancellationToken);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;// Do something with the response...&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Now the only step I am missing is what string or prompt do I want to pass to OpenAI?&lt;/p&gt;
&lt;p&gt;I want a social media post that will fit into a facebook/twitter/bluesky etc post, some of these services are character limited so I have included a character limit in my &amp;ldquo;prompt&amp;rdquo;, however I am being clever and counting the number of characters taken up by the URL and hashtags and subtracting that first.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-csharp" data-lang="csharp"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;string&lt;/span&gt; socialMediaPrompt = &lt;span style="color:#e6db74"&gt;$&amp;#34;write a social media post under {240 - length} characters, excluding hash tags about {feedItem.Description}&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Now once I put is all together I have a page that loads my rss feed, and I can click a button to generate a social media post.&lt;/p&gt;
&lt;p&gt;
&lt;img class="img-fluid" alt="Ai Example" src="https://blog-dev.funkysi1701.com/images/ai-example.png" loading="lazy"
width="1843" height="432"
/&gt;
&lt;/p&gt;
&lt;h2 id="conclusion"&gt;Conclusion&lt;a class="anchor ms-1" href="#conclusion" aria-label="Permalink: Conclusion"&gt;&lt;i class="fas fa-link" aria-hidden="true"&gt;&lt;/i&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;By combining .NET, Semantic Kernel, and OpenAI, I was able to automate the creation of social media posts, saving time and effort. This approach can be adapted for other use cases, such as summarizing content or generating marketing copy.&lt;/p&gt;
&lt;p&gt;If you have enjoyed this article and want to get a monthly email with all my latest articles, please sign up to my &lt;a href="https://blog-dev.funkysi1701.com/newsletter"&gt;newsletter&lt;/a&gt;
. If you have any questions or want to share your own experiences, feel free to leave a comment below!&lt;/p&gt;</description></item><item><title>Merge two Git projects, keep history</title><link>https://blog-dev.funkysi1701.com/posts/2025/merge-two-projects-into-one/</link><author>funkysi1701@gmail.com (funkysi1701)</author><pubDate>Mon, 10 Mar 2025 20:00:00 +0000</pubDate><guid>https://blog-dev.funkysi1701.com/posts/2025/merge-two-projects-into-one/</guid><category term="Git">Git</category><category term="VersionControl">VersionControl</category><category term="RepositoryManagement">RepositoryManagement</category><category term="Development">Development</category><category term="Tech">Tech</category><category term="Programming">Programming</category><media:content medium="image" type="image/png" url="https://blog-dev.funkysi1701.com/images/git-merge.png"/><description>&lt;p&gt;I needed to merge two separate projects into one repository while keeping the full commit history from both sides. Copy-paste would have worked for the files, but history would have stayed trapped in the old remotes.&lt;/p&gt;
&lt;p&gt;This approach adds the second repo as a remote, fetches it, and merges with &lt;code&gt;--allow-unrelated-histories&lt;/code&gt; so Git keeps both lineages. I still refer to the repos as &lt;code&gt;project1&lt;/code&gt; and &lt;code&gt;project2&lt;/code&gt;, with remotes at &lt;code&gt;project1Url&lt;/code&gt; and &lt;code&gt;project2Url&lt;/code&gt;.&lt;/p&gt;
&lt;h2 id="step-1-clone-the-original-repository"&gt;Step 1: Clone the original repository&lt;a class="anchor ms-1" href="#step-1-clone-the-original-repository" aria-label="Permalink: Step 1: Clone the original repository"&gt;&lt;i class="fas fa-link" aria-hidden="true"&gt;&lt;/i&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;First, clone the repository of &lt;code&gt;project1&lt;/code&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;git clone project1Url
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="step-2-add-the-new-repository-as-a-remote"&gt;Step 2: Add the new repository as a remote&lt;a class="anchor ms-1" href="#step-2-add-the-new-repository-as-a-remote" aria-label="Permalink: Step 2: Add the new repository as a remote"&gt;&lt;i class="fas fa-link" aria-hidden="true"&gt;&lt;/i&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Next, add the repository of &lt;code&gt;project2&lt;/code&gt; as a remote:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;git remote add project2 project2Url
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="step-3-fetch-the-commits-from-the-new-repository"&gt;Step 3: Fetch the commits from the new repository&lt;a class="anchor ms-1" href="#step-3-fetch-the-commits-from-the-new-repository" aria-label="Permalink: Step 3: Fetch the commits from the new repository"&gt;&lt;i class="fas fa-link" aria-hidden="true"&gt;&lt;/i&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Fetch the commits from the &lt;code&gt;project2&lt;/code&gt; repository:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;git fetch project2
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="step-4-merge-the-commits-from-the-new-repository"&gt;Step 4: Merge the commits from the new repository&lt;a class="anchor ms-1" href="#step-4-merge-the-commits-from-the-new-repository" aria-label="Permalink: Step 4: Merge the commits from the new repository"&gt;&lt;i class="fas fa-link" aria-hidden="true"&gt;&lt;/i&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Merge the commits from the &lt;code&gt;project2&lt;/code&gt; repository into &lt;code&gt;project1&lt;/code&gt;. Point at the branch that holds the code you want (often &lt;code&gt;main&lt;/code&gt; or &lt;code&gt;develop&lt;/code&gt;):&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;git merge project2/develop --allow-unrelated-histories
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="step-5-fix-merge-conflicts"&gt;Step 5: Fix merge conflicts&lt;a class="anchor ms-1" href="#step-5-fix-merge-conflicts" aria-label="Permalink: Step 5: Fix merge conflicts"&gt;&lt;i class="fas fa-link" aria-hidden="true"&gt;&lt;/i&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;If there are conflicts, you will need to resolve them. In my case, this was mainly build files and scripts in the root of the repository. Most of the important code was in subfolders, so this was minimal.&lt;/p&gt;
&lt;h2 id="step-6-push-the-changes-to-the-original-repository"&gt;Step 6: Push the changes to the original repository&lt;a class="anchor ms-1" href="#step-6-push-the-changes-to-the-original-repository" aria-label="Permalink: Step 6: Push the changes to the original repository"&gt;&lt;i class="fas fa-link" aria-hidden="true"&gt;&lt;/i&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Push the changes to a new branch for review before merging into the main branch:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;git checkout -b feature/merge-project2-into-project1
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;git push origin feature/merge-project2-into-project1
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="step-7-clean-up"&gt;Step 7: Clean up&lt;a class="anchor ms-1" href="#step-7-clean-up" aria-label="Permalink: Step 7: Clean up"&gt;&lt;i class="fas fa-link" aria-hidden="true"&gt;&lt;/i&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Test your code to ensure everything builds and runs as expected now that it is all in one repository. Create a pull request and review all the changes to ensure nothing gets changed that shouldn&amp;rsquo;t be.&lt;/p&gt;
&lt;p&gt;When you look at your PR, it will look like a lot of code has been added. However, if you look closely at the Git history of those files, you will see that the history has been preserved from the original repository. You can remove the temporary &lt;code&gt;project2&lt;/code&gt; remote once you no longer need it (&lt;code&gt;git remote remove project2&lt;/code&gt;).&lt;/p&gt;
&lt;h2 id="conclusion"&gt;Conclusion&lt;a class="anchor ms-1" href="#conclusion" aria-label="Permalink: Conclusion"&gt;&lt;i class="fas fa-link" aria-hidden="true"&gt;&lt;/i&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;This is still one of the simplest ways to combine two codebases without throwing away history. Use it when consolidating services or folding a spike repo into the main project; open a PR so reviewers can see the merge conflicts and folder layout before it lands on the default branch.&lt;/p&gt;
&lt;h3 id="related-on-this-blog"&gt;Related on this blog&lt;a class="anchor ms-1" href="#related-on-this-blog" aria-label="Permalink: Related on this blog"&gt;&lt;i class="fas fa-link" aria-hidden="true"&gt;&lt;/i&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Once the histories live in one repo, automated promotion helps: see &lt;a href="https://blog-dev.funkysi1701.com/posts/2024/automatic-pull-requests/"&gt;automatic pull requests&lt;/a&gt;
for GitHub Actions and Azure DevOps patterns, and &lt;a href="https://blog-dev.funkysi1701.com/posts/2022/using-github-actions/"&gt;using GitHub Actions&lt;/a&gt;
if you are wiring CI around the merge.&lt;/p&gt;
&lt;p&gt;If you have enjoyed this article and want to get a monthly email with all my latest articles, please sign up for my &lt;a href="https://blog-dev.funkysi1701.com/newsletter"&gt;newsletter&lt;/a&gt;
. If you have any questions or comments, please feel free to reach out or leave a comment below.&lt;/p&gt;</description></item></channel></rss>