Note that there are some explanatory texts on larger screens.

plurals
  1. POwhy does git allow remote tags to move, or why you can't use git-tag for atomic test-and-set
    text
    copied!<p>I have a problem where two similar processes are running in parallel within separate clones of the same repository (typically on different computers). Each time a process runs, it fetches the latest tags from the remote and then deduces a unique number based on the tags it sees.</p> <p>E.g. if these tags exist on the remote: 1.0 1.1 1.2 1.3 then a process will choose 1.4 as the next number.</p> <p>Before the process starts, it creates a new tag and pushes this back to the remote:</p> <pre><code>$ git tag 1.4 HEAD $ git push origin tag 1.4 </code></pre> <p>The <strong>idea</strong> was that this is a way to atomically select numbers. The other process, if it's looking at the same time, might also decide to use 1.4, but when it comes to push it's tag, it should discover that 1.4 already exists, and choose 1.5 instead (and try again).</p> <p>My hope was that I could treat git tag pushes as atomic.</p> <p>Unfortunately, for some weird reason, git allows remote tags to move in certain circumstances!</p> <p>For example, let's say tag 1.4 has been put on origin/master and pushed. The other process wants to put tag 1.4 on, say, origin/master^, which would involve moving the tag backwards. Git will reject this with a 'non-fast-forward' error:</p> <p>Process A:</p> <pre><code>$ git tag 1.4 origin/master $ git push origin tag 1.4 Total 0 (delta 0), reused 0 (delta 0) To /repo1 * [new tag] 1.4 -&gt; 1.4 </code></pre> <p>Process B:</p> <pre><code>$ git tag 1.4 origin/master^ $ git push origin tag 1.4 To /repo1 ! [rejected] 1.4 -&gt; 1.4 (non-fast forward) error: failed to push some refs to '/repo1' </code></pre> <p>Ok, that's fine, Process B can use this to try 1.5 instead.</p> <p>But consider this situation:</p> <p>Process A:</p> <pre><code>$ git tag 1.4 origin/master $ git push origin tag 1.4 Total 0 (delta 0), reused 0 (delta 0) To /repo1 * [new tag] 1.4 -&gt; 1.4 </code></pre> <p>Process B:</p> <pre><code>$ git tag 1.4 origin/master $ git push origin tag 1.4 Everything up-to-date </code></pre> <p>Oh. That's a shame - git didn't indicate that this tag already exists on the remote. Actually, it does, with -v:</p> <pre><code>$ git push origin tag 1.4 -v Pushing to /repo1 To /repo1 = [up to date] 1.4 -&gt; 1.4 Everything up-to-date </code></pre> <p>Ok, so I can do some sort of stderr redirect, search for " = ", and that will allow Process B to determine that 1.4 is already in use.</p> <p>But that's a bit silly. And it gets worse:</p> <p>Process A:</p> <pre><code>$ git push origin tag 1.4 Total 0 (delta 0), reused 0 (delta 0) To /repo1 * [new tag] 1.4 -&gt; 1.4 </code></pre> <p>Process B:</p> <pre><code>$ git push origin tag 1.4 Total 0 (delta 0), reused 0 (delta 0) To /repo1 fd0e09e..c6cdac9 1.4 -&gt; 1.4 </code></pre> <p><strong>Argg!</strong> What? Git has just moved the remote tag without warning!</p> <p>So it seems to me that remote tags in git are fundamentally broken - they shouldn't just "move" without an explicit request. More to the point, they should refuse to move by default.</p> <p>Also, the git-tag command should provide a way to atomically test-and-set a tag.</p> <p>But clearly it doesn't. Running git fetch first isn't going to help because there's still a window of conflict and even if there is a conflict, in one of the three scenarios the tag simply moves!</p> <p>What is going on here?</p> <p>Is there another way to test-and-set a tag?</p> <p>If not, how do people allocate and reserve build numbers in an automated build environment? How do you reliably detect when two processes have inadvertently picked up the same build number?</p> <p>Using git 1.6.1.2.</p>
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload