From e7f8f8829c1990a24f030fafe68907e6836f3af7 Mon Sep 17 00:00:00 2001
From: Cristian Maglie <c.maglie@arduino.cc>
Date: Mon, 7 Apr 2025 17:17:08 +0200
Subject: [PATCH 1/3] Added integration test

---
 internal/integrationtest/lib/lib_test.go | 21 +++++++++++++++++++++
 1 file changed, 21 insertions(+)

diff --git a/internal/integrationtest/lib/lib_test.go b/internal/integrationtest/lib/lib_test.go
index 15371c80299..f900a416dad 100644
--- a/internal/integrationtest/lib/lib_test.go
+++ b/internal/integrationtest/lib/lib_test.go
@@ -16,6 +16,8 @@
 package lib_test
 
 import (
+	"crypto/sha256"
+	"encoding/hex"
 	"encoding/json"
 	"fmt"
 	"io"
@@ -687,6 +689,7 @@ func TestInstallWithGitUrlFragmentAsBranch(t *testing.T) {
 
 	t.Run("RefPointingToBranch", func(t *testing.T) {
 		libInstallDir := cli.SketchbookDir().Join("libraries", "ArduinoCloud")
+		t.Cleanup(func() { libInstallDir.RemoveAll() })
 
 		// Verify install with ref pointing to a branch
 		require.NoDirExists(t, libInstallDir.String())
@@ -700,6 +703,24 @@ func TestInstallWithGitUrlFragmentAsBranch(t *testing.T) {
 		require.NoError(t, err)
 		require.Contains(t, string(fileToTest), `#define LENGHT_M "meters"`) // nolint:misspell
 	})
+
+	t.Run("RefPointingToHash", func(t *testing.T) {
+		libInstallDir := cli.SketchbookDir().Join("libraries", "ArduinoCloud")
+		t.Cleanup(func() { libInstallDir.RemoveAll() })
+
+		// Verify install with ref pointing to a branch
+		require.NoDirExists(t, libInstallDir.String())
+		_, _, err = cli.Run("lib", "install", "--git-url", "https://github.com/arduino-libraries/ArduinoCloud.git#fe1a1c5d1f8ea2cb27ece1a3b9344dc1eaed60b6", "--config-file", "arduino-cli.yaml")
+		require.NoError(t, err)
+		require.DirExists(t, libInstallDir.String())
+
+		// Verify that the correct branch is checked out
+		// https://github.com/arduino-libraries/ArduinoCloud/commit/fe1a1c5d1f8ea2cb27ece1a3b9344dc1eaed60b6
+		fileToTest, err := libInstallDir.Join("examples", "ReadAndWrite", "ReadAndWrite.ino").ReadFile()
+		require.NoError(t, err)
+		chksum := sha256.Sum256(fileToTest)
+		require.Equal(t, hex.EncodeToString(chksum[:]), `f71889cd6da3b91755c7d1b8ec76b7ee6e2824d8a417c043d117ffdf1546f896`)
+	})
 }
 
 func TestUpdateIndex(t *testing.T) {

From 95ee7ae85d62711e6cf80e311851505bc5dae27a Mon Sep 17 00:00:00 2001
From: Cristian Maglie <c.maglie@arduino.cc>
Date: Mon, 7 Apr 2025 17:18:44 +0200
Subject: [PATCH 2/3] bugfix: allow lib-install from git with revision hash

---
 .../libraries/librariesmanager/install.go     | 31 ++++++++++++-------
 1 file changed, 20 insertions(+), 11 deletions(-)

diff --git a/internal/arduino/libraries/librariesmanager/install.go b/internal/arduino/libraries/librariesmanager/install.go
index 1d73849f81f..b32a117e0c9 100644
--- a/internal/arduino/libraries/librariesmanager/install.go
+++ b/internal/arduino/libraries/librariesmanager/install.go
@@ -20,7 +20,6 @@ import (
 	"errors"
 	"fmt"
 	"net/url"
-	"os"
 	"strings"
 
 	"github.com/arduino/arduino-cli/commands/cmderrors"
@@ -215,17 +214,27 @@ func (lmi *Installer) InstallGitLib(argURL string, overwrite bool) error {
 	defer tmp.RemoveAll()
 	tmpInstallPath := tmp.Join(libraryName)
 
-	depth := 1
-	if ref != "" {
-		depth = 0
-	}
 	if _, err := git.PlainClone(tmpInstallPath.String(), false, &git.CloneOptions{
 		URL:           gitURL,
-		Depth:         depth,
-		Progress:      os.Stdout,
-		ReferenceName: ref,
+		ReferenceName: plumbing.ReferenceName(ref),
 	}); err != nil {
-		return err
+		if err.Error() != "reference not found" {
+			return err
+		}
+
+		// We did not find the requested reference, let's do a PlainClone and use
+		// "ResolveRevision" to find and checkout the requested revision
+		if repo, err := git.PlainClone(tmpInstallPath.String(), false, &git.CloneOptions{
+			URL: gitURL,
+		}); err != nil {
+			return err
+		} else if h, err := repo.ResolveRevision(plumbing.Revision(ref)); err != nil {
+			return err
+		} else if w, err := repo.Worktree(); err != nil {
+			return err
+		} else if err := w.Checkout(&git.CheckoutOptions{Hash: plumbing.NewHash(h.String())}); err != nil {
+			return err
+		}
 	}
 
 	// We don't want the installed library to be a git repository thus we delete this folder
@@ -241,7 +250,7 @@ func (lmi *Installer) InstallGitLib(argURL string, overwrite bool) error {
 
 // parseGitArgURL tries to recover a library name from a git URL.
 // Returns an error in case the URL is not a valid git URL.
-func parseGitArgURL(argURL string) (string, string, plumbing.ReferenceName, error) {
+func parseGitArgURL(argURL string) (string, string, string, error) {
 	// On Windows handle paths with backslashes in the form C:\Path\to\library
 	if path := paths.New(argURL); path != nil && path.Exist() {
 		return path.Base(), argURL, "", nil
@@ -279,7 +288,7 @@ func parseGitArgURL(argURL string) (string, string, plumbing.ReferenceName, erro
 		return "", "", "", errors.New(i18n.Tr("invalid git url"))
 	}
 	// fragment == "1.0.3"
-	rev := plumbing.ReferenceName(parsedURL.Fragment)
+	rev := parsedURL.Fragment
 	// gitURL == "https://github.com/arduino-libraries/SigFox.git"
 	parsedURL.Fragment = ""
 	gitURL := parsedURL.String()

From 39aca21288cc1b9fb296f668637cbf3fcfcee978 Mon Sep 17 00:00:00 2001
From: Cristian Maglie <c.maglie@arduino.cc>
Date: Mon, 7 Apr 2025 18:04:42 +0200
Subject: [PATCH 3/3] Workaround for a bug in go-git

---
 internal/arduino/libraries/librariesmanager/install.go | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/internal/arduino/libraries/librariesmanager/install.go b/internal/arduino/libraries/librariesmanager/install.go
index b32a117e0c9..9b53910d41a 100644
--- a/internal/arduino/libraries/librariesmanager/install.go
+++ b/internal/arduino/libraries/librariesmanager/install.go
@@ -232,7 +232,9 @@ func (lmi *Installer) InstallGitLib(argURL string, overwrite bool) error {
 			return err
 		} else if w, err := repo.Worktree(); err != nil {
 			return err
-		} else if err := w.Checkout(&git.CheckoutOptions{Hash: plumbing.NewHash(h.String())}); err != nil {
+		} else if err := w.Checkout(&git.CheckoutOptions{
+			Force: true, // workaround for: https://github.com/go-git/go-git/issues/1411
+			Hash:  plumbing.NewHash(h.String())}); err != nil {
 			return err
 		}
 	}