<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Hristiyan Dodov</title>
    <description>The latest articles on DEV Community by Hristiyan Dodov (@dodov).</description>
    <link>https://dev.to/dodov</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F881725%2Fdf56e675-1b10-4160-8b79-83e90241f1b5.jpeg</url>
      <title>DEV Community: Hristiyan Dodov</title>
      <link>https://dev.to/dodov</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/dodov"/>
    <language>en</language>
    <item>
      <title>Debug Browser Redirects Without Ruining Your Day</title>
      <dc:creator>Hristiyan Dodov</dc:creator>
      <pubDate>Sun, 28 May 2023 13:52:42 +0000</pubDate>
      <link>https://dev.to/dodov/debug-browser-redirects-without-ruining-your-day-14a4</link>
      <guid>https://dev.to/dodov/debug-browser-redirects-without-ruining-your-day-14a4</guid>
      <description>&lt;p&gt;Redirects are bad for your website visitors because they result in excess HTTP requests which make pages load slower. Because they contribute to a bad user experience, your SEO will also suffer and you'll rank lower in Google Search.&lt;/p&gt;

&lt;p&gt;Unfortunately, redirects can be tricky to debug because you instantly go to the destination URL, without much time to think about or even acknowledge what's happening.&lt;/p&gt;

&lt;p&gt;Obtaining information is crucial, otherwise you're surrounded by uncertainty, nothing makes sense, and you get frustrated. Having more tools at your disposal gives you more options to test and leads you to a solution way faster and easier.&lt;/p&gt;

&lt;h2&gt;
  
  
  DevTools
&lt;/h2&gt;

&lt;p&gt;The first tool I'd recommend is a tool you probably &lt;em&gt;already have&lt;/em&gt;, and it's in its name — &lt;a href="https://developer.chrome.com/docs/devtools/"&gt;&lt;strong&gt;Dev&lt;/strong&gt;Tools&lt;/a&gt;. It can be useful for a lot of things, redirects included:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Open DevTools with &lt;code&gt;Ctrl + Shift + I&lt;/code&gt; on Windows and &lt;code&gt;Cmd + Option + I&lt;/code&gt; on Mac (or using &lt;a href="https://developer.chrome.com/docs/devtools/open/#shortcuts"&gt;one of the other ways&lt;/a&gt;).&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Toggle the "Network" panel.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Check &lt;a href="https://developer.chrome.com/docs/devtools/network/reference/#disable-cache"&gt;"Disable cache"&lt;/a&gt;, so that browser caching is disabled and doesn't interfere with your changes.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Optionally check &lt;a href="https://developer.chrome.com/docs/devtools/network/reference/#preserve-log"&gt;"Preserve log"&lt;/a&gt; as well, so that request logs are not getting cleared upon page refresh.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Optionally &lt;a href="https://developer.chrome.com/docs/devtools/network/reference/#filter-by-type"&gt;filter requests by type&lt;/a&gt; "Doc", meaning that only HTML document requests are shown, which represent the initial request for a page.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--CIuwuWra--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/e7pyjhpzn6seav85ubs1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--CIuwuWra--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/e7pyjhpzn6seav85ubs1.png" alt='Chrome DevTools "Network" panel' width="800" height="548"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As you can see, DevTools provides very useful information about the request that resulted in a redirect:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Status"&gt;HTTP status code&lt;/a&gt; — whether it was a 301 redirect, 302, etc.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cache-Control"&gt;&lt;code&gt;Cache-Control&lt;/code&gt; header&lt;/a&gt; — whether the browser should cache the request, how, and for how long&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://developers.cloudflare.com/cache/about/default-cache-behavior/#cloudflare-cache-responses"&gt;&lt;code&gt;CF-Cache-Status&lt;/code&gt; header&lt;/a&gt; — whether the response has been cached by &lt;a href="https://www.cloudflare.com/what-is-cloudflare/"&gt;Cloudflare&lt;/a&gt; and how&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Location"&gt;&lt;code&gt;Location&lt;/code&gt; header&lt;/a&gt; — the destination URL of the redirect&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you've fixed the problem, but it just "won't work", then you might be facing a caching issue and my &lt;a href="https://dodov.dev/blog/browser-cache-and-edge-cache-explained"&gt;explanation for browser cache and edge cache&lt;/a&gt; could help you understand what's going on.&lt;/p&gt;

&lt;p&gt;It's also important to note that browsers have special, more aggressive caching behavior when it comes to 301 redirects, as explained in &lt;a href="https://stackoverflow.com/a/21396547/3130281"&gt;this Stack Overflow answer&lt;/a&gt;:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;In the absense of cache control directives that specify otherwise, a 301 redirect defaults to being cached without any expiry date.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Where DevTools falls short, however, is when you're dealing with a client-side &lt;a href="https://www.semrush.com/blog/javascript-redirect/"&gt;JavaScript redirect&lt;/a&gt;. DevTools won't keep the HTML document body upon landing on the destination URL and you won't be able to inspect the JavaScript of the page in question to figure out what happened. Despite having the "Preserve log" setting enabled, DevTools still won't capture the body:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Qjs1xXPx--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/c0el51kaaafjbihs6yiw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Qjs1xXPx--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/c0el51kaaafjbihs6yiw.png" alt='Empty "Response" tab in DevTools due to JavaScript redirect' width="800" height="491"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;To get around that, you could prefix the problematic URL with &lt;code&gt;view-source:&lt;/code&gt; in your browser's address bar and hit "Enter". This will display the HTML of the page, but without rendering it (and without executing the scripts inside):&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--q7pFJE8X--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ghf8espgszpoqvtjtm81.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--q7pFJE8X--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ghf8espgszpoqvtjtm81.png" alt="Chrome  raw `view-source` endraw  screen" width="800" height="426"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now, you can see the script that causes the redirect. The above example is very minimal and obvious, however. In the real world, the actual line of JavaScript that redirects might be hidden in externally loaded scripts, so you'd have to search those as well.&lt;/p&gt;

&lt;h2&gt;
  
  
  Browser Extension
&lt;/h2&gt;

&lt;p&gt;Using DevTools and view-source is useful when you have a concrete issue that you want to resolve. But redirects happen so quickly that sometimes you might not even notice that something had happened at all. And how likely is it that you'll casually be viewing requests in DevTools and looking at their status codes…&lt;/p&gt;

&lt;p&gt;This is where the &lt;a href="https://chrome.google.com/webstore/detail/link-redirect-trace/nnpljppamoaalgkieeciijbcccohlpoh"&gt;Link Redirect Trace&lt;/a&gt; browser extension can help you out. When you navigate to a page, it keeps track of all redirects that occurred during the process, allowing you to see the chain of events:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--nWcKgOv5--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/1xhv9cy02623kkdabp7k.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--nWcKgOv5--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/1xhv9cy02623kkdabp7k.png" alt="Link Redirect Trace extension popup" width="800" height="535"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The cool thing is that it will also display any redirects that may have occurred as you casually browse the site. All you have to do is to glance over at the extension's badge and see if it displays anything other than "200":&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--KFbCUHtB--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/p7szr1bcbx968z9vkgdf.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--KFbCUHtB--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/p7szr1bcbx968z9vkgdf.png" alt="Link Redirect Trace extension badge" width="764" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  cURL
&lt;/h2&gt;

&lt;p&gt;Using the browser can lengthen the feedback loop while fixing something — you need to make the change, switch to the browser, refresh the page… and you might also forget to disable the browser cache, which can confuse you tremendously.&lt;/p&gt;

&lt;p&gt;For this reason, you can use the &lt;a href="https://curl.se/"&gt;cURL&lt;/a&gt; program. It does just a single job — make a request and print out a response. It's quick, does not have caching mechanisms (like the browser), and runs in the terminal, which is likely already by your side while working.&lt;/p&gt;

&lt;p&gt;Using the &lt;code&gt;-i&lt;/code&gt; flag, you can print the full response of a request:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-i&lt;/span&gt; https://dodov.dev/test-redirect-js
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight http"&gt;&lt;code&gt;&lt;span class="err"&gt;HTTP/2 200 
date: Sat, 29 Apr 2023 05:51:51 GMT
cache-control: public, max-age=1800
last-modified: Sat, 29 Apr 2023 05:50:22 GMT
cf-cache-status: HIT

&amp;lt;script&amp;gt;window.location.href = "/blog/how-to-debug-browser-redirects";&amp;lt;/script&amp;gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As you can see, you get everything valuable in one place — the HTTP status code, the headers, and the body. You could also very easily rerun the command again and see the potential changes.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; I've removed some of the response headers in these examples because they're not relevant to what we're talking about.&lt;/p&gt;

&lt;p&gt;Here's what we get for a 301 redirect:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-i&lt;/span&gt; https://dodov.dev/test-redirect-301
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight http"&gt;&lt;code&gt;&lt;span class="err"&gt;HTTP/2 301 
date: Sat, 29 Apr 2023 06:01:30 GMT
cache-control: public, max-age=1800
location: /blog/how-to-debug-browser-redirects
cf-cache-status: HIT
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This time, the HTTP status code is &lt;code&gt;301&lt;/code&gt;, rather than &lt;code&gt;200&lt;/code&gt;, and instead of a response body with a script tag, we get a &lt;code&gt;location&lt;/code&gt; header that determines the URL destination.&lt;/p&gt;

&lt;h3&gt;
  
  
  Printing only headers
&lt;/h3&gt;

&lt;p&gt;By default, cURL returns the response body and the &lt;code&gt;-i&lt;/code&gt; flag makes it include the response headers as well. But unless you're dealing with a JavaScript redirect, you'll only want to be looking at the headers and the response body would do nothing more than to clutter your terminal.&lt;/p&gt;

&lt;p&gt;To print out just the headers with cURL, you have to use the &lt;code&gt;-I&lt;/code&gt; flag (note that it's capital "I", not &lt;code&gt;-i&lt;/code&gt;), which only prints out the document info (headers). However, this also changes the request method to &lt;a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods/HEAD"&gt;&lt;code&gt;HEAD&lt;/code&gt;&lt;/a&gt;, and the browser issues &lt;code&gt;GET&lt;/code&gt; requests for HTML documents, so it makes sense to use the &lt;code&gt;-X&lt;/code&gt; flag as well and execute a &lt;code&gt;GET&lt;/code&gt; request instead:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-I&lt;/span&gt; &lt;span class="nt"&gt;-X&lt;/span&gt; GET https://example.com/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Copy as cURL
&lt;/h3&gt;

&lt;p&gt;There's a quick and easy way to transition from testing redirects in the browser to testing them with cURL. You can right-click on a request in the "Network" panel, then click "Copy as cURL":&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--XN2HIPtm--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/a3u7e8f6xzxjzzsrjqcg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--XN2HIPtm--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/a3u7e8f6xzxjzzsrjqcg.png" alt='DevTools "Copy as cURL" option' width="800" height="549"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;After that, you'd be able to paste a cURL command which issues a request that is identical to the one the browser would send, with all of the appropriate headers:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="s1"&gt;'https://dodov.dev/test-redirect-301'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s1"&gt;'authority: dodov.dev'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s1"&gt;'accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s1"&gt;'accept-language: en-US,en;q=0.9'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s1"&gt;'cache-control: no-cache'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s1"&gt;'cookie: _ga=GA1.1.236846401.1677490962; _ga_6LMHZGGEMZ=GS1.1.1682655158.6.0.1682655162.0.0.0'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s1"&gt;'pragma: no-cache'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s1"&gt;'sec-ch-ua: "Chromium";v="112", "Google Chrome";v="112", "Not:A-Brand";v="99"'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s1"&gt;'sec-ch-ua-mobile: ?0'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s1"&gt;'sec-ch-ua-platform: "macOS"'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s1"&gt;'sec-fetch-dest: document'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s1"&gt;'sec-fetch-mode: navigate'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s1"&gt;'sec-fetch-site: none'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s1"&gt;'sec-fetch-user: ?1'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s1"&gt;'upgrade-insecure-requests: 1'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s1"&gt;'user-agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/112.0.0.0 Safari/537.36'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--compressed&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is really useful, because you can comment out or change various headers and see how that would affect the reponse. You basically can't do that in the browser at all.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Redirects are important because they negatively impact user experience and SEO. They're also tricky to discover and debug because they're subtle and instantaneous. But with the right tools, you can catch and fix them quite easily:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Use &lt;a href="https://chrome.google.com/webstore/detail/link-redirect-trace/nnpljppamoaalgkieeciijbcccohlpoh"&gt;Link Redirect Trace&lt;/a&gt; to spontaneously find redirects and get a quick overview of what had happened&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Use &lt;a href="https://developer.chrome.com/docs/devtools/"&gt;DevTools&lt;/a&gt; to get more advanced details about the requests, such as the headers&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Use &lt;a href="https://curl.se/"&gt;cURL&lt;/a&gt; to get in the weeds and experiment more freely and rapidly, while avoiding cache pitfalls&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;No matter what you use, make sure you &lt;strong&gt;always&lt;/strong&gt; test in the browser &lt;strong&gt;as well&lt;/strong&gt; when you're done. That's where your users are.&lt;/p&gt;

&lt;p&gt;If you found that useful, you can tune in to my upcoming advices and suggestions by following me on &lt;a href="https://twitter.com/yandodov"&gt;Twitter&lt;/a&gt; or &lt;a href="https://www.linkedin.com/in/yandodov/"&gt;LinkedIn&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>productivity</category>
      <category>tooling</category>
    </item>
    <item>
      <title>How Can Git Stash Cause a Conflict?</title>
      <dc:creator>Hristiyan Dodov</dc:creator>
      <pubDate>Sun, 27 Nov 2022 20:49:12 +0000</pubDate>
      <link>https://dev.to/dodov/how-can-git-stash-cause-a-conflict-38kk</link>
      <guid>https://dev.to/dodov/how-can-git-stash-cause-a-conflict-38kk</guid>
      <description>&lt;p&gt;I've had several occasions where I'd receive a conflict after popping a git stash. But how does it make any sense? A stash is not attached to any commit. It's just the previously saved state of one file, slapped on top of the current state of the file.&lt;/p&gt;

&lt;p&gt;As &lt;a href="https://stackoverflow.com/a/44360581/3130281" rel="noopener noreferrer"&gt;answered on Stack Overflow&lt;/a&gt;:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;To get a merge conflict within one file in the work-tree, Git must see the &lt;em&gt;same line&lt;/em&gt; changed by both left and right side versions, but changed &lt;em&gt;in different ways&lt;/em&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;On the surface, that doesn't apply here, because there is no "left" and "right" side. You're not merging one branch (left) with another branch (right). You are instead applying a stash, which is not related to any branch or commit. Why couldn't Git just "paste" the stashed contents of the file on top of the current ones?&lt;/p&gt;

&lt;p&gt;The &lt;a href="https://git-scm.com/docs/git-stash#_description" rel="noopener noreferrer"&gt;git stash documentation&lt;/a&gt; states:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Use &lt;code&gt;git stash&lt;/code&gt; when you want to record the current state of the working directory and the index, but want to go back to a clean working directory.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The answer lies in what "current state" means. It implies that a stash simply stores the current state of the file. But that's not what actually happens. It stores the &lt;strong&gt;changes&lt;/strong&gt; inside the file. And a change represents the &lt;em&gt;transition&lt;/em&gt; between &lt;em&gt;two distinct states&lt;/em&gt;. This means that for each file, a stash relates to both the stashed change &lt;strong&gt;and&lt;/strong&gt; the original version of the file at the time of stashing.&lt;/p&gt;

&lt;p&gt;Let's say you have two branches, both containing a file with the contents "foo". You're on feature branch "feat", make some changes, stash them, then pop them on branch "main". It might look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;main&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;          &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;feat&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="n"&gt;foo&lt;/span&gt;             &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;foo&lt;/span&gt;
                &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;foo&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;bar&lt;/span&gt;   &lt;span class="c1"&gt;// file change&lt;/span&gt;
                &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="err"&gt;$&lt;/span&gt; &lt;span class="n"&gt;git&lt;/span&gt; &lt;span class="n"&gt;stash&lt;/span&gt;  &lt;span class="c1"&gt;// stash that "foo" becomes "bar"&lt;/span&gt;
&lt;span class="err"&gt;$&lt;/span&gt; &lt;span class="n"&gt;git&lt;/span&gt; &lt;span class="n"&gt;stash&lt;/span&gt; &lt;span class="n"&gt;pop&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;              &lt;span class="c1"&gt;// pop the stash on branch "main"&lt;/span&gt;
&lt;span class="n"&gt;foo&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;bar&lt;/span&gt;      &lt;span class="o"&gt;|&lt;/span&gt;              &lt;span class="c1"&gt;// "foo" changes to "bar"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this case, everything worked alright. But let's suppose that we make a commit on branch "feat" that &lt;em&gt;changes the content&lt;/em&gt;, before stashing a new change and popping it on "main":&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;main&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;          &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;feat&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="n"&gt;foo&lt;/span&gt;             &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;foo&lt;/span&gt;
                &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;foo&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;bar2&lt;/span&gt;  &lt;span class="c1"&gt;// file change&lt;/span&gt;
                &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="err"&gt;$&lt;/span&gt; &lt;span class="n"&gt;git&lt;/span&gt; &lt;span class="n"&gt;commit&lt;/span&gt; &lt;span class="c1"&gt;// commit "foo" changing to "bar2"&lt;/span&gt;
                &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;bar2&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;bar3&lt;/span&gt; &lt;span class="c1"&gt;// file change&lt;/span&gt;
                &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="err"&gt;$&lt;/span&gt; &lt;span class="n"&gt;git&lt;/span&gt; &lt;span class="n"&gt;stash&lt;/span&gt;  &lt;span class="c1"&gt;// stash that "2" becomes "3"&lt;/span&gt;
&lt;span class="err"&gt;$&lt;/span&gt; &lt;span class="n"&gt;git&lt;/span&gt; &lt;span class="n"&gt;stash&lt;/span&gt; &lt;span class="n"&gt;pop&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;              &lt;span class="c1"&gt;// pop the stash on branch "main"&lt;/span&gt;
&lt;span class="n"&gt;foo&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;CONFLICT&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;  &lt;span class="o"&gt;|&lt;/span&gt;              &lt;span class="c1"&gt;// change "2" to "3" in "foo"?&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, we get a conflict. If it were an actual file, we'd see this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;&lt;span class="gd"&gt;&amp;lt;&amp;lt;&amp;lt;&amp;lt;&amp;lt;&amp;lt;&amp;lt; Updated upstream
&lt;/span&gt;&lt;span class="p"&gt;foo
&lt;/span&gt;&lt;span class="gh"&gt;=======
&lt;/span&gt;&lt;span class="p"&gt;bar3
&lt;/span&gt;&lt;span class="gi"&gt;&amp;gt;&amp;gt;&amp;gt;&amp;gt;&amp;gt;&amp;gt;&amp;gt; Stashed changes
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Why does that happen? Well, in the first scenario, we:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Stash that "foo" becomes "bar"&lt;/li&gt;
&lt;li&gt;Pop it on "main", where we still have "foo"&lt;/li&gt;
&lt;li&gt;Git can successfully change "foo" to "bar"&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;However, in the second scenario, we:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Commit that "foo" becomes "bar2"&lt;/li&gt;
&lt;li&gt;Stash that "2" becomes "3"&lt;/li&gt;
&lt;li&gt;Pop it on "main", where we still have "foo"&lt;/li&gt;
&lt;li&gt;Git &lt;em&gt;can't&lt;/em&gt; change "2" to "3" because there is no "2"!&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;So this is where the left and right side thing mentioned earlier comes into play. We have "foo" in our worktree on "main" (the left side), "bar2" as the original content of the stashed file (the right side), and a stash that states "2" turns to "3":&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[left (main)]  [stash (change)]  [right (original)]
foo            2 -&amp;gt; 3            bar2
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This confuses Git, because changing "2" to "3" only makes sense on the right side. On the left, &lt;em&gt;there is no "2"&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;On the other hand, in the first example, we have "foo" on both sides, and the change from "foo" to "bar" in the stash:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[left (main)]  [stash (change)]  [right (original)]
foo            foo -&amp;gt; bar        foo
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here, Git has no issues, because it compares apples to apples and allows you to turn them into oranges. It's not trying to turn oranges into tangerines, when all you have is apples.&lt;/p&gt;

&lt;p&gt;Here's how the second scenario would pan out if we didn't commit:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;main&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;          &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;feat&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="n"&gt;foo&lt;/span&gt;             &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;foo&lt;/span&gt;
                &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;foo&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;bar2&lt;/span&gt;  &lt;span class="c1"&gt;// file change&lt;/span&gt;
                &lt;span class="o"&gt;|&lt;/span&gt;              &lt;span class="c1"&gt;// do not commit "foo" to "bar2"&lt;/span&gt;
                &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;bar2&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;bar3&lt;/span&gt; &lt;span class="c1"&gt;// file change&lt;/span&gt;
                &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="err"&gt;$&lt;/span&gt; &lt;span class="n"&gt;git&lt;/span&gt; &lt;span class="n"&gt;stash&lt;/span&gt;  &lt;span class="c1"&gt;// stash that "foo" becomes "bar3"&lt;/span&gt;
&lt;span class="err"&gt;$&lt;/span&gt; &lt;span class="n"&gt;git&lt;/span&gt; &lt;span class="n"&gt;stash&lt;/span&gt; &lt;span class="n"&gt;pop&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;              &lt;span class="c1"&gt;// pop the stash on branch "main"&lt;/span&gt;
&lt;span class="n"&gt;foo&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;bar3&lt;/span&gt;     &lt;span class="o"&gt;|&lt;/span&gt;              &lt;span class="c1"&gt;// "foo" changes to "bar3"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, we:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Stash "foo" becoming "bar3", not the "2" in "bar2" becoming a "3"&lt;/li&gt;
&lt;li&gt;Pop it on "main", where we still have "foo"&lt;/li&gt;
&lt;li&gt;Git can successfully change "foo" to "bar3"&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;We're comparing apples to apples again:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[left (main)]  [stash (change)]  [right (original)]
foo            foo -&amp;gt; bar3       foo
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;…and Git can yet again apply the stash successfully.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;When stashing, you don't just save the current state of the file. You save the &lt;em&gt;transition&lt;/em&gt; from one initial state to another state. If you pop the stash someplace where the current state differs from the initial state of the stash, you get a conflict.&lt;/p&gt;

&lt;p&gt;If you liked my explanation, you can follow &lt;a href="https://twitter.com/yandodov" rel="noopener noreferrer"&gt;@yandodov&lt;/a&gt; on Twitter. I enjoy dissecting problems like these and sharing my conclusions! 🤙&lt;/p&gt;

</description>
      <category>elixir</category>
    </item>
  </channel>
</rss>
