Repository: stackitcloud/stackit-cli
Pull request #725: Add stackit git commands: list, describe, create and delete.
Author: login: peremanent
Created: 2025-05-05T11:41:11Z | Merged: 2025-05-13T11:10:48Z

## Description

<!-- **Please link some issue here describing what you are trying to achieve.**

In case there is no issue present for your PR, please consider creating one.
At least please give us some description what you are trying to achieve and why your change is needed. -->

* Added the following commands for Stackit Git service:
   * list, describe, create and delete.

## Checklist

- [ ] Issue was linked above
- [ ] Code format was applied: `make fmt`
- [ ] Examples were added / adjusted (see e.g. [here](https://github.com/stackitcloud/stackit-cli/blob/ef291d1683ca5b0d719ec0a26ecb999a32685117/internal/cmd/ske/cluster/create/create.go#L49-L63))
- [x] Docs are up-to-date: `make generate-docs` (will be checked by CI)
- [ ] Unit tests got implemented or updated
- [x] Unit tests are passing: `make test` (will be checked by CI)
- [x] No linter issues: `make lint` (will be checked by CI) 



Diff:
diff --git a/docs/stackit.md b/docs/stackit.md
index 2dc456b83..3cc35f2f9 100644
--- a/docs/stackit.md
+++ b/docs/stackit.md
@@ -33,6 +33,7 @@ stackit [flags]
 * [stackit config](./stackit_config.md)	 - Provides functionality for CLI configuration options
 * [stackit curl](./stackit_curl.md)	 - Executes an authenticated HTTP request to an endpoint
 * [stackit dns](./stackit_dns.md)	 - Provides functionality for DNS
+* [stackit git](./stackit_git.md)	 - Provides functionality for STACKIT Git
 * [stackit image](./stackit_image.md)	 - Manage server images
 * [stackit key-pair](./stackit_key-pair.md)	 - Provides functionality for SSH key pairs
 * [stackit load-balancer](./stackit_load-balancer.md)	 - Provides functionality for Load Balancer
diff --git a/docs/stackit_git.md b/docs/stackit_git.md
new file mode 100644
index 000000000..8a959c7d2
--- /dev/null
+++ b/docs/stackit_git.md
@@ -0,0 +1,37 @@
+## stackit git
+
+Provides functionality for STACKIT Git
+
+### Synopsis
+
+Provides functionality for STACKIT Git.
+
+```
+stackit git [flags]
+```
+
+### Options
+
+```
+  -h, --help   Help for "stackit git"
+```
+
+### Options inherited from parent commands
+
+```
+  -y, --assume-yes             If set, skips all confirmation prompts
+      --async                  If set, runs the command asynchronously
+  -o, --output-format string   Output format, one of ["json" "pretty" "none" "yaml"]
+  -p, --project-id string      Project ID
+      --region string          Target region for region-specific requests
+      --verbosity string       Verbosity of the CLI, one of ["debug" "info" "warning" "error"] (default "info")
+```
+
+### SEE ALSO
+
+* [stackit](./stackit.md)	 - Manage STACKIT resources using the command line
+* [stackit git create](./stackit_git_create.md)	 - Creates STACKIT Git instance
+* [stackit git delete](./stackit_git_delete.md)	 - Deletes STACKIT Git instance
+* [stackit git describe](./stackit_git_describe.md)	 - Describes STACKIT Git instance
+* [stackit git list](./stackit_git_list.md)	 - Lists all instances of STACKIT Git.
+
diff --git a/docs/stackit_git_create.md b/docs/stackit_git_create.md
new file mode 100644
index 000000000..fa7a2a97d
--- /dev/null
+++ b/docs/stackit_git_create.md
@@ -0,0 +1,41 @@
+## stackit git create
+
+Creates STACKIT Git instance
+
+### Synopsis
+
+Create a STACKIT Git instance by name.
+
+```
+stackit git create [flags]
+```
+
+### Examples
+
+```
+  Create a instance with name 'my-new-instance'
+  $ stackit git create --name my-new-instance
+```
+
+### Options
+
+```
+  -h, --help          Help for "stackit git create"
+      --name string   The name of the instance.
+```
+
+### Options inherited from parent commands
+
+```
+  -y, --assume-yes             If set, skips all confirmation prompts
+      --async                  If set, runs the command asynchronously
+  -o, --output-format string   Output format, one of ["json" "pretty" "none" "yaml"]
+  -p, --project-id string      Project ID
+      --region string          Target region for region-specific requests
+      --verbosity string       Verbosity of the CLI, one of ["debug" "info" "warning" "error"] (default "info")
+```
+
+### SEE ALSO
+
+* [stackit git](./stackit_git.md)	 - Provides functionality for STACKIT Git
+
diff --git a/docs/stackit_git_delete.md b/docs/stackit_git_delete.md
new file mode 100644
index 000000000..6f863ee60
--- /dev/null
+++ b/docs/stackit_git_delete.md
@@ -0,0 +1,40 @@
+## stackit git delete
+
+Deletes STACKIT Git instance
+
+### Synopsis
+
+Deletes a STACKIT Git instance by its internal ID.
+
+```
+stackit git delete INSTANCE_ID [flags]
+```
+
+### Examples
+
+```
+  Delete a instance with ID "xxx"
+  $ stackit git delete xxx
+```
+
+### Options
+
+```
+  -h, --help   Help for "stackit git delete"
+```
+
+### Options inherited from parent commands
+
+```
+  -y, --assume-yes             If set, skips all confirmation prompts
+      --async                  If set, runs the command asynchronously
+  -o, --output-format string   Output format, one of ["json" "pretty" "none" "yaml"]
+  -p, --project-id string      Project ID
+      --region string          Target region for region-specific requests
+      --verbosity string       Verbosity of the CLI, one of ["debug" "info" "warning" "error"] (default "info")
+```
+
+### SEE ALSO
+
+* [stackit git](./stackit_git.md)	 - Provides functionality for STACKIT Git
+
diff --git a/docs/stackit_git_describe.md b/docs/stackit_git_describe.md
new file mode 100644
index 000000000..e2aedcc40
--- /dev/null
+++ b/docs/stackit_git_describe.md
@@ -0,0 +1,40 @@
+## stackit git describe
+
+Describes STACKIT Git instance
+
+### Synopsis
+
+Describes a STACKIT Git instance by its internal ID.
+
+```
+stackit git describe INSTANCE_ID [flags]
+```
+
+### Examples
+
+```
+  Describe instance "xxx"
+  $ stackit git describe xxx
+```
+
+### Options
+
+```
+  -h, --help   Help for "stackit git describe"
+```
+
+### Options inherited from parent commands
+
+```
+  -y, --assume-yes             If set, skips all confirmation prompts
+      --async                  If set, runs the command asynchronously
+  -o, --output-format string   Output format, one of ["json" "pretty" "none" "yaml"]
+  -p, --project-id string      Project ID
+      --region string          Target region for region-specific requests
+      --verbosity string       Verbosity of the CLI, one of ["debug" "info" "warning" "error"] (default "info")
+```
+
+### SEE ALSO
+
+* [stackit git](./stackit_git.md)	 - Provides functionality for STACKIT Git
+
diff --git a/docs/stackit_git_list.md b/docs/stackit_git_list.md
new file mode 100644
index 000000000..c0f65927b
--- /dev/null
+++ b/docs/stackit_git_list.md
@@ -0,0 +1,44 @@
+## stackit git list
+
+Lists all instances of STACKIT Git.
+
+### Synopsis
+
+Lists all instances of STACKIT Git for the current project.
+
+```
+stackit git list [flags]
+```
+
+### Examples
+
+```
+  List all STACKIT Git instances
+  $ stackit git instance list
+
+  Lists up to 10 STACKIT Git instances
+  $ stackit git instance list --limit=10
+```
+
+### Options
+
+```
+  -h, --help        Help for "stackit git list"
+      --limit int   Limit the output to the first n elements
+```
+
+### Options inherited from parent commands
+
+```
+  -y, --assume-yes             If set, skips all confirmation prompts
+      --async                  If set, runs the command asynchronously
+  -o, --output-format string   Output format, one of ["json" "pretty" "none" "yaml"]
+  -p, --project-id string      Project ID
+      --region string          Target region for region-specific requests
+      --verbosity string       Verbosity of the CLI, one of ["debug" "info" "warning" "error"] (default "info")
+```
+
+### SEE ALSO
+
+* [stackit git](./stackit_git.md)	 - Provides functionality for STACKIT Git
+
diff --git a/go.mod b/go.mod
index d4fa80e97..5e4de09de 100644
--- a/go.mod
+++ b/go.mod
@@ -19,6 +19,7 @@ require (
 	github.com/stackitcloud/stackit-sdk-go/services/alb v0.2.3
 	github.com/stackitcloud/stackit-sdk-go/services/authorization v0.6.3
 	github.com/stackitcloud/stackit-sdk-go/services/dns v0.13.3
+	github.com/stackitcloud/stackit-sdk-go/services/git v0.3.3
 	github.com/stackitcloud/stackit-sdk-go/services/iaas v0.22.2
 	github.com/stackitcloud/stackit-sdk-go/services/mongodbflex v1.0.1
 	github.com/stackitcloud/stackit-sdk-go/services/opensearch v0.21.2
diff --git a/go.sum b/go.sum
index 8735251de..d16afaad1 100644
--- a/go.sum
+++ b/go.sum
@@ -570,6 +570,8 @@ github.com/stackitcloud/stackit-sdk-go/services/authorization v0.6.3 h1:aXVMNdiH
 github.com/stackitcloud/stackit-sdk-go/services/authorization v0.6.3/go.mod h1:dJ19ZwFjp2bfC5ZobXV3vUdSpE3quUw3GuoFSKLpHIo=
 github.com/stackitcloud/stackit-sdk-go/services/dns v0.13.3 h1:WJnA6YTOlBxMJf9PRuxkXduxj+fyWi+wOks69PvN1qI=
 github.com/stackitcloud/stackit-sdk-go/services/dns v0.13.3/go.mod h1:PMHoavoIaRZpkI9BA0nsnRjGoHASVSBon45XB3QyhMA=
+github.com/stackitcloud/stackit-sdk-go/services/git v0.3.3 h1:/NXxiJf/NFpj+DFXc07vsx+0he/xufdhIskXUlSeyyw=
+github.com/stackitcloud/stackit-sdk-go/services/git v0.3.3/go.mod h1:XhXHJpOVC9Rpwyf1G+EpMbprBafH9aZb8vWBdR+z0WM=
 github.com/stackitcloud/stackit-sdk-go/services/iaas v0.22.2 h1:zh6e2eHgqex++fr6N0RK7wMMBGesC3QhBd42FdTq2Z8=
 github.com/stackitcloud/stackit-sdk-go/services/iaas v0.22.2/go.mod h1:QNH50Pq0Hu21lLDOwa02PIjRjTl0LfEdHoz5snGQRn8=
 github.com/stackitcloud/stackit-sdk-go/services/loadbalancer v1.0.3 h1:StQg5p4h1aLFwIHQWWe5l44aFue+ZvQMGDbypP1DMYw=
diff --git a/internal/cmd/git/create/create.go b/internal/cmd/git/create/create.go
new file mode 100644
index 000000000..6df750b37
--- /dev/null
+++ b/internal/cmd/git/create/create.go
@@ -0,0 +1,167 @@
+package create
+
+import (
+	"context"
+	"encoding/json"
+	"fmt"
+
+	"github.com/stackitcloud/stackit-cli/internal/cmd/params"
+
+	"github.com/goccy/go-yaml"
+	"github.com/spf13/cobra"
+	"github.com/stackitcloud/stackit-cli/internal/pkg/args"
+	"github.com/stackitcloud/stackit-cli/internal/pkg/errors"
+	"github.com/stackitcloud/stackit-cli/internal/pkg/examples"
+	"github.com/stackitcloud/stackit-cli/internal/pkg/flags"
+	"github.com/stackitcloud/stackit-cli/internal/pkg/globalflags"
+	"github.com/stackitcloud/stackit-cli/internal/pkg/print"
+	"github.com/stackitcloud/stackit-cli/internal/pkg/services/git/client"
+	"github.com/stackitcloud/stackit-cli/internal/pkg/spinner"
+	"github.com/stackitcloud/stackit-cli/internal/pkg/utils"
+	"github.com/stackitcloud/stackit-sdk-go/services/git"
+	"github.com/stackitcloud/stackit-sdk-go/services/git/wait"
+)
+
+const (
+	nameFlag = "name"
+)
+
+type inputModel struct {
+	*globalflags.GlobalFlagModel
+	Id   *string
+	Name string
+}
+
+func NewCmd(params *params.CmdParams) *cobra.Command {
+	cmd := &cobra.Command{
+		Use:   "create",
+		Short: "Creates STACKIT Git instance",
+		Long:  "Create a STACKIT Git instance by name.",
+		Args:  args.NoArgs,
+		Example: examples.Build(
+			examples.NewExample(
+				`Create a instance with name 'my-new-instance'`,
+				`$ stackit git create --name my-new-instance`,
+			),
+		),
+		RunE: func(cmd *cobra.Command, _ []string) (err error) {
+			ctx := context.Background()
+			model, err := parseInput(params.Printer, cmd)
+			if err != nil {
+				return err
+			}
+
+			// Configure API client
+			apiClient, err := client.ConfigureClient(params.Printer)
+			if err != nil {
+				return err
+			}
+
+			if !model.AssumeYes {
+				prompt := fmt.Sprintf("Are you sure you want to create the instance %q?", model.Name)
+				err = params.Printer.PromptForConfirmation(prompt)
+				if err != nil {
+					return err
+				}
+			}
+
+			// Call API
+			request := buildRequest(ctx, model, apiClient)
+
+			result, err := request.Execute()
+			if err != nil {
+				return fmt.Errorf("create stackit git instance: %w", err)
+			}
+			model.Id = result.Id
+
+			// Wait for async operation, if async mode not enabled
+			if !model.Async {
+				s := spinner.New(params.Printer)
+				s.Start("Creating stackit git instance")
+				_, err = wait.CreateGitInstanceWaitHandler(ctx, apiClient, model.ProjectId, *model.Id).WaitWithContext(ctx)
+				if err != nil {
+					return fmt.Errorf("wait for stackit git Instance creation: %w", err)
+				}
+				s.Stop()
+			}
+
+			return outputResult(params.Printer, model, result)
+		},
+	}
+
+	configureFlags(cmd)
+	return cmd
+}
+
+func configureFlags(cmd *cobra.Command) {
+	cmd.Flags().String(nameFlag, "", "The name of the instance.")
+	if err := flags.MarkFlagsRequired(cmd, nameFlag); err != nil {
+		cobra.CheckErr(err)
+	}
+}
+
+func parseInput(p *print.Printer, cmd *cobra.Command) (*inputModel, error) {
+	globalFlags := globalflags.Parse(p, cmd)
+
+	if globalFlags.ProjectId == "" {
+		return nil, &errors.ProjectIdError{}
+	}
+	name := flags.FlagToStringValue(p, cmd, nameFlag)
+
+	model := inputModel{
+		GlobalFlagModel: globalFlags,
+		Name:            name,
+	}
+
+	if p.IsVerbosityDebug() {
+		modelStr, err := print.BuildDebugStrFromInputModel(model)
+		if err != nil {
+			p.Debug(print.ErrorLevel, "convert model to string for debugging: %v", err)
+		} else {
+			p.Debug(print.DebugLevel, "parsed input values: %s", modelStr)
+		}
+	}
+
+	return &model, nil
+}
+
+func buildRequest(ctx context.Context, model *inputModel, apiClient *git.APIClient) git.ApiCreateInstanceRequest {
+	return apiClient.CreateInstance(ctx, model.ProjectId).CreateInstancePayload(createPayload(model))
+}
+
+func createPayload(model *inputModel) git.CreateInstancePayload {
+	return git.CreateInstancePayload{
+		Name: &model.Name,
+	}
+}
+
+func outputResult(p *print.Printer, model *inputModel, resp *git.Instance) error {
+	if model == nil {
+		return fmt.Errorf("input model is nil")
+	}
+	var outputFormat string
+	if model.GlobalFlagModel != nil {
+		outputFormat = model.OutputFormat
+	}
+	switch outputFormat {
+	case print.JSONOutputFormat:
+		details, err := json.MarshalIndent(resp, "", "  ")
+		if err != nil {
+			return fmt.Errorf("marshal instance: %w", err)
+		}
+		p.Outputln(string(details))
+
+		return nil
+	case print.YAMLOutputFormat:
+		details, err := yaml.MarshalWithOptions(resp, yaml.IndentSequence(true), yaml.UseJSONMarshaler())
+		if err != nil {
+			return fmt.Errorf("marshal iminstanceage: %w", err)
+		}
+		p.Outputln(string(details))
+
+		return nil
+	default:
+		p.Outputf("Created instance %q with id %s\n", model.Name, utils.PtrString(model.Id))
+		return nil
+	}
+}
diff --git a/internal/cmd/git/create/create_test.go b/internal/cmd/git/create/create_test.go
new file mode 100644
index 000000000..33ed0cc7e
--- /dev/null
+++ b/internal/cmd/git/create/create_test.go
@@ -0,0 +1,257 @@
+package create
+
+import (
+	"context"
+	"testing"
+
+	"github.com/google/go-cmp/cmp"
+	"github.com/google/go-cmp/cmp/cmpopts"
+	"github.com/google/uuid"
+	"github.com/stackitcloud/stackit-cli/internal/cmd/params"
+	"github.com/stackitcloud/stackit-cli/internal/pkg/globalflags"
+	"github.com/stackitcloud/stackit-cli/internal/pkg/print"
+	"github.com/stackitcloud/stackit-cli/internal/pkg/utils"
+	"github.com/stackitcloud/stackit-sdk-go/services/git"
+)
+
+type testCtxKey struct{}
+
+var (
+	testCtx       = context.WithValue(context.Background(), testCtxKey{}, "foo")
+	testClient    = &git.APIClient{}
+	testProjectId = uuid.NewString()
+
+	testName = "test-instance"
+)
+
+func fixtureFlagValues(mods ...func(flagValues map[string]string)) map[string]string {
+	flagValues := map[string]string{
+		globalflags.ProjectIdFlag: testProjectId,
+
+		nameFlag: testName,
+	}
+	for _, mod := range mods {
+		mod(flagValues)
+	}
+	return flagValues
+}
+
+func fixtureInputModel(mods ...func(model *inputModel)) *inputModel {
+	model := &inputModel{
+		GlobalFlagModel: &globalflags.GlobalFlagModel{ProjectId: testProjectId, Verbosity: globalflags.VerbosityDefault},
+		Name:            testName,
+	}
+	for _, mod := range mods {
+		mod(model)
+	}
+	return model
+}
+
+func fixtureCreatePayload(mods ...func(payload *git.CreateInstancePayload)) (payload git.CreateInstancePayload) {
+	payload = git.CreateInstancePayload{
+		Name: &testName,
+	}
+	for _, mod := range mods {
+		mod(&payload)
+	}
+	return payload
+}
+
+func fixtureRequest(mods ...func(request *git.ApiCreateInstanceRequest)) git.ApiCreateInstanceRequest {
+	request := testClient.CreateInstance(testCtx, testProjectId)
+
+	request = request.CreateInstancePayload(fixtureCreatePayload())
+
+	for _, mod := range mods {
+		mod(&request)
+	}
+	return request
+}
+
+func TestParseInput(t *testing.T) {
+	tests := []struct {
+		description   string
+		flagValues    map[string]string
+		isValid       bool
+		expectedModel *inputModel
+	}{
+		{
+			description:   "base",
+			flagValues:    fixtureFlagValues(),
+			isValid:       true,
+			expectedModel: fixtureInputModel(),
+		},
+		{
+			description: "no values",
+			flagValues:  map[string]string{},
+			isValid:     false,
+		},
+		{
+			description: "project id missing",
+			flagValues: fixtureFlagValues(func(flagValues map[string]string) {
+				delete(flagValues, globalflags.ProjectIdFlag)
+			}),
+			isValid: false,
+		},
+		{
+			description: "project id invalid 1",
+			flagValues: fixtureFlagValues(func(flagValues map[string]string) {
+				flagValues[globalflags.ProjectIdFlag] = ""
+			}),
+			isValid: false,
+		},
+		{
+			description: "project id invalid 2",
+			flagValues: fixtureFlagValues(func(flagValues map[string]string) {
+				flagValues[globalflags.ProjectIdFlag] = "invalid-uuid"
+			}),
+			isValid: false,
+		},
+		{
+			description: "name missing",
+			flagValues: fixtureFlagValues(func(flagValues map[string]string) {
+				delete(flagValues, nameFlag)
+			}),
+			isValid: false,
+		},
+	}
+
+	for _, tt := range tests {
+		t.Run(tt.description, func(t *testing.T) {
+			p := print.NewPrinter()
+			cmd := NewCmd(&params.CmdParams{Printer: p})
+			if err := globalflags.Configure(cmd.Flags()); err != nil {
+				t.Errorf("cannot configure global flags: %v", err)
+			}
+
+			for flag, value := range tt.flagValues {
+				err := cmd.Flags().Set(flag, value)
+				if err != nil {
+					if !tt.isValid {
+						return
+					}
+					t.Fatalf("setting flag --%s=%s: %v", flag, value, err)
+				}
+			}
+
+			if err := cmd.ValidateFlagGroups(); err != nil {
+				if !tt.isValid {
+					return
+				}
+				t.Fatalf("error validating flag groups: %v", err)
+			}
+
+			if err := cmd.ValidateRequiredFlags(); err != nil {
+				if !tt.isValid {
+					return
+				}
+				t.Fatalf("error validating flags: %v", err)
+			}
+
+			model, err := parseInput(p, cmd)
+			if err != nil {
+				if !tt.isValid {
+					return
+				}
+				t.Fatalf("error parsing flags: %v", err)
+			}
+
+			if !tt.isValid {
+				t.Fatalf("did not fail on invalid input")
+			}
+			diff := cmp.Diff(model, tt.expectedModel)
+			if diff != "" {
+				t.Fatalf("Data does not match: %s", diff)
+			}
+		})
+	}
+}
+
+func TestBuildRequest(t *testing.T) {
+	tests := []struct {
+		description     string
+		model           *inputModel
+		expectedRequest git.ApiCreateInstanceRequest
+	}{
+		{
+			description:     "base",
+			model:           fixtureInputModel(),
+			expectedRequest: fixtureRequest(),
+		},
+		{
+			description: "name flag",
+			model: fixtureInputModel(func(model *inputModel) {
+				model.Name = "new-name"
+			}),
+			expectedRequest: fixtureRequest(func(request *git.ApiCreateInstanceRequest) {
+				*request = request.CreateInstancePayload(fixtureCreatePayload(func(payload *git.CreateInstancePayload) {
+					payload.Name = utils.Ptr("new-name")
+				}))
+			}),
+		},
+	}
+
+	for _, tt := range tests {
+		t.Run(tt.description, func(t *testing.T) {
+			request := buildRequest(testCtx, tt.model, testClient)
+			diff := cmp.Diff(request, tt.expectedRequest,
+				cmp.AllowUnexported(tt.expectedRequest),
+				cmpopts.EquateComparable(testCtx),
+				cmp.AllowUnexported(git.NullableString{}),
+			)
+			if diff != "" {
+				t.Fatalf("Data does not match: %s", diff)
+			}
+		})
+	}
+}
+
+func TestOutputResult(t *testing.T) {
+	type args struct {
+		model *inputModel
+		resp  *git.Instance
+	}
+	tests := []struct {
+		name    string
+		args    args
+		wantErr bool
+	}{
+		{
+			name: "nil",
+			args: args{
+				model: nil,
+				resp:  nil,
+			},
+			wantErr: true,
+		},
+		{
+			name: "empty input",
+			args: args{
+				model: &inputModel{},
+				resp:  &git.Instance{},
+			},
+			wantErr: false,
+		},
+		{
+			name: "output json",
+			args: args{
+				model: &inputModel{
+					GlobalFlagModel: &globalflags.GlobalFlagModel{
+						OutputFormat: print.JSONOutputFormat,
+					},
+				},
+				resp: nil,
+			},
+			wantErr: false,
+		},
+	}
+	p := print.NewPrinter()
+	p.Cmd = NewCmd(&params.CmdParams{Printer: p})
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			if err := outputResult(p, tt.args.model, tt.args.resp); (err != nil) != tt.wantErr {
+				t.Errorf("outputResult() error = %v, wantErr %v", err, tt.wantErr)
+			}
+		})
+	}
+}
diff --git a/internal/cmd/git/delete/delete.go b/internal/cmd/git/delete/delete.go
new file mode 100644
index 000000000..67ae11438
--- /dev/null
+++ b/internal/cmd/git/delete/delete.go
@@ -0,0 +1,132 @@
+package delete
+
+import (
+	"context"
+	"fmt"
+
+	"github.com/stackitcloud/stackit-cli/internal/cmd/params"
+
+	"github.com/spf13/cobra"
+	"github.com/stackitcloud/stackit-cli/internal/pkg/args"
+	"github.com/stackitcloud/stackit-cli/internal/pkg/errors"
+	"github.com/stackitcloud/stackit-cli/internal/pkg/examples"
+	"github.com/stackitcloud/stackit-cli/internal/pkg/globalflags"
+	"github.com/stackitcloud/stackit-cli/internal/pkg/print"
+	"github.com/stackitcloud/stackit-cli/internal/pkg/projectname"
+	"github.com/stackitcloud/stackit-cli/internal/pkg/services/git/client"
+	gitUtils "github.com/stackitcloud/stackit-cli/internal/pkg/services/git/utils"
+	"github.com/stackitcloud/stackit-cli/internal/pkg/spinner"
+	"github.com/stackitcloud/stackit-cli/internal/pkg/utils"
+	"github.com/stackitcloud/stackit-sdk-go/services/git"
+	"github.com/stackitcloud/stackit-sdk-go/services/git/wait"
+)
+
+type inputModel struct {
+	*globalflags.GlobalFlagModel
+	InstanceId string
+}
+
+const instanceIdArg = "INSTANCE_ID"
+
+func NewCmd(params *params.CmdParams) *cobra.Command {
+	cmd := &cobra.Command{
+		Use:   fmt.Sprintf("delete %s", instanceIdArg),
+		Short: "Deletes STACKIT Git instance",
+		Long:  "Deletes a STACKIT Git instance by its internal ID.",
+		Args:  args.SingleArg(instanceIdArg, utils.ValidateUUID),
+		Example: examples.Build(
+			examples.NewExample(`Delete a instance with ID "xxx"`, `$ stackit git delete xxx`),
+		),
+		RunE: func(cmd *cobra.Command, args []string) error {
+			ctx := context.Background()
+			model, err := parseInput(params.Printer, cmd, args)
+			if err != nil {
+				return err
+			}
+
+			// Configure API client
+			apiClient, err := client.ConfigureClient(params.Printer)
+			if err != nil {
+				return err
+			}
+
+			projectName, err := projectname.GetProjectName(ctx, params.Printer, params.CliVersion, cmd)
+			if err != nil {
+				params.Printer.Debug(print.ErrorLevel, "get project name: %v", err)
+				projectName = model.ProjectId
+			}
+
+			instanceName, err := gitUtils.GetInstanceName(ctx, apiClient, model.ProjectId, model.InstanceId)
+			if err != nil {
+				params.Printer.Debug(print.ErrorLevel, "get stackit git intance name: %v", err)
+				instanceName = model.InstanceId
+			} else if instanceName == "" {
+				instanceName = model.InstanceId
+			}
+
+			if !model.AssumeYes {
+				prompt := fmt.Sprintf("Are you sure you want to delete the stackit git instance %q for %q?", instanceName, projectName)
+				err = params.Printer.PromptForConfirmation(prompt)
+				if err != nil {
+					return err
+				}
+			}
+
+			// Call API
+			request := buildRequest(ctx, model, apiClient)
+
+			err = request.Execute()
+			if err != nil {
+				return fmt.Errorf("delete instance: %w", err)
+			}
+
+			// Wait for async operation, if async mode not enabled
+			if !model.Async {
+				s := spinner.New(params.Printer)
+				s.Start("Deleting stackit git instance")
+				_, err = wait.DeleteGitInstanceWaitHandler(ctx, apiClient, model.ProjectId, model.InstanceId).WaitWithContext(ctx)
+				if err != nil {
+					return fmt.Errorf("wait for stackit git instance deletion: %w", err)
+				}
+				s.Stop()
+			}
+
+			operationState := "Deleted"
+			if model.Async {
+				operationState = "Triggered deletion of"
+			}
+			params.Printer.Info("%s stackit git instance %s \n", operationState, model.InstanceId)
+
+			return nil
+		},
+	}
+
+	return cmd
+}
+
+func parseInput(p *print.Printer, cmd *cobra.Command, cliArgs []string) (*inputModel, error) {
+	globalFlags := globalflags.Parse(p, cmd)
+	if globalFlags.ProjectId == "" {
+		return nil, &errors.ProjectIdError{}
+	}
+
+	model := inputModel{
+		GlobalFlagModel: globalFlags,
+		InstanceId:      cliArgs[0],
+	}
+
+	if p.IsVerbosityDebug() {
+		modelStr, err := print.BuildDebugStrFromInputModel(model)
+		if err != nil {
+			p.Debug(print.ErrorLevel, "convert model to string for debugging: %v", err)
+		} else {
+			p.Debug(print.DebugLevel, "parsed input values: %s", modelStr)
+		}
+	}
+
+	return &model, nil
+}
+
+func buildRequest(ctx context.Context, model *inputModel, apiClient *git.APIClient) git.ApiDeleteInstanceRequest {
+	return apiClient.DeleteInstance(ctx, model.ProjectId, model.InstanceId)
+}
diff --git a/internal/cmd/git/delete/delete_test.go b/internal/cmd/git/delete/delete_test.go
new file mode 100644
index 000000000..8c90a4f1d
--- /dev/null
+++ b/internal/cmd/git/delete/delete_test.go
@@ -0,0 +1,182 @@
+package delete
+
+import (
+	"context"
+	"testing"
+
+	"github.com/stackitcloud/stackit-cli/internal/cmd/params"
+	"github.com/stackitcloud/stackit-cli/internal/pkg/globalflags"
+	"github.com/stackitcloud/stackit-cli/internal/pkg/print"
+
+	"github.com/google/go-cmp/cmp"
+	"github.com/google/go-cmp/cmp/cmpopts"
+	"github.com/google/uuid"
+	"github.com/stackitcloud/stackit-sdk-go/services/git"
+)
+
+type testCtxKey struct{}
+
+var (
+	testCtx        = context.WithValue(context.Background(), testCtxKey{}, "foo")
+	testClient     = &git.APIClient{}
+	testProjectId  = uuid.NewString()
+	testInstanceId = uuid.NewString()
+)
+
+func fixtureFlagValues(mods ...func(flagValues map[string]string)) map[string]string {
+	flagValues := map[string]string{
+		globalflags.ProjectIdFlag: testProjectId,
+	}
+	for _, mod := range mods {
+		mod(flagValues)
+	}
+	return flagValues
+}
+
+func fixtureInputModel(mods ...func(model *inputModel)) *inputModel {
+	model := &inputModel{
+		GlobalFlagModel: &globalflags.GlobalFlagModel{ProjectId: testProjectId, Verbosity: globalflags.VerbosityDefault},
+		InstanceId:      testInstanceId,
+	}
+	for _, mod := range mods {
+		mod(model)
+	}
+	return model
+}
+
+func fixtureRequest(mods ...func(request *git.ApiDeleteInstanceRequest)) git.ApiDeleteInstanceRequest {
+	request := testClient.DeleteInstance(testCtx, testProjectId, testInstanceId)
+	for _, mod := range mods {
+		mod(&request)
+	}
+	return request
+}
+
+func TestParseInput(t *testing.T) {
+	tests := []struct {
+		description   string
+		flagValues    map[string]string
+		args          []string
+		isValid       bool
+		expectedModel *inputModel
+	}{
+		{
+			description:   "base",
+			flagValues:    fixtureFlagValues(),
+			args:          []string{testInstanceId},
+			isValid:       true,
+			expectedModel: fixtureInputModel(),
+		},
+		{
+			description: "project id invalid 1",
+			flagValues: fixtureFlagValues(func(flagValues map[string]string) {
+				flagValues[globalflags.ProjectIdFlag] = ""
+			}),
+			isValid: false,
+		},
+		{
+			description: "project id invalid 2",
+			flagValues: fixtureFlagValues(func(flagValues map[string]string) {
+				flagValues[globalflags.ProjectIdFlag] = "invalid-uuid"
+			}),
+			isValid: false,
+		},
+		{
+			description: "no arguments",
+			flagValues:  fixtureFlagValues(),
+			args:        nil,
+			isValid:     false,
+		},
+		{
+			description: "multiple arguments",
+			flagValues:  fixtureFlagValues(),
+			args:        []string{"foo", "bar"},
+			isValid:     false,
+		},
+		{
+			description: "invalid instance id",
+			flagValues:  fixtureFlagValues(),
+			args:        []string{"foo"},
+			isValid:     false,
+		},
+	}
+
+	for _, tt := range tests {
+		t.Run(tt.description, func(t *testing.T) {
+			p := print.NewPrinter()
+			cmd := NewCmd(&params.CmdParams{Printer: p})
+			err := globalflags.Configure(cmd.Flags())
+			if err != nil {
+				t.Fatalf("configure global flags: %v", err)
+			}
+			cmd.SetArgs(tt.args)
+
+			for flag, value := range tt.flagValues {
+				err := cmd.Flags().Set(flag, value)
+				if err != nil {
+					if !tt.isValid {
+						return
+					}
+					t.Fatalf("setting flag --%s=%s: %v", flag, value, err)
+				}
+			}
+
+			if err := cmd.ValidateArgs(tt.args); err != nil {
+				if !tt.isValid {
+					return
+				}
+			}
+
+			err = cmd.ValidateRequiredFlags()
+			if err != nil {
+				if !tt.isValid {
+					return
+				}
+				t.Fatalf("error validating flags: %v", err)
+			}
+
+			model, err := parseInput(p, cmd, tt.args)
+			if err != nil {
+				if !tt.isValid {
+					return
+				}
+				t.Fatalf("error parsing flags: %v", err)
+			}
+
+			if !tt.isValid {
+				t.Fatalf("did not fail on invalid input")
+			}
+			diff := cmp.Diff(model, tt.expectedModel)
+			if diff != "" {
+				t.Fatalf("Data does not match: %s", diff)
+			}
+		})
+	}
+}
+
+func TestBuildRequest(t *testing.T) {
+	tests := []struct {
+		description     string
+		model           *inputModel
+		expectedRequest git.ApiDeleteInstanceRequest
+	}{
+		{
+			description:     "base",
+			model:           fixtureInputModel(),
+			expectedRequest: fixtureRequest(),
+		},
+	}
+
+	for _, tt := range tests {
+		t.Run(tt.description, func(t *testing.T) {
+			request := buildRequest(testCtx, tt.model, testClient)
+			diff := cmp.Diff(request, tt.expectedRequest,
+				cmp.AllowUnexported(tt.expectedRequest),
+				cmpopts.EquateComparable(testCtx),
+			)
+			if diff != "" {
+				t.Fatalf("Data does not match: %s", diff)
+			}
+		})
+	}
+}
diff --git a/internal/cmd/git/describe/describe.go b/internal/cmd/git/describe/describe.go
new file mode 100644
index 000000000..8e2f266e1
--- /dev/null
+++ b/internal/cmd/git/describe/describe.go
@@ -0,0 +1,152 @@
+package describe
+
+import (
+	"context"
+	"encoding/json"
+	"fmt"
+
+	"github.com/stackitcloud/stackit-cli/internal/cmd/params"
+
+	"github.com/goccy/go-yaml"
+	"github.com/spf13/cobra"
+	"github.com/stackitcloud/stackit-cli/internal/pkg/args"
+	"github.com/stackitcloud/stackit-cli/internal/pkg/errors"
+	"github.com/stackitcloud/stackit-cli/internal/pkg/examples"
+	"github.com/stackitcloud/stackit-cli/internal/pkg/globalflags"
+	"github.com/stackitcloud/stackit-cli/internal/pkg/print"
+	"github.com/stackitcloud/stackit-cli/internal/pkg/services/git/client"
+	"github.com/stackitcloud/stackit-cli/internal/pkg/tables"
+	"github.com/stackitcloud/stackit-cli/internal/pkg/utils"
+	"github.com/stackitcloud/stackit-sdk-go/services/git"
+)
+
+type inputModel struct {
+	*globalflags.GlobalFlagModel
+	InstanceId string
+}
+
+const instanceIdArg = "INSTANCE_ID"
+
+func NewCmd(params *params.CmdParams) *cobra.Command {
+	cmd := &cobra.Command{
+		Use:   fmt.Sprintf("describe %s", instanceIdArg),
+		Short: "Describes STACKIT Git instance",
+		Long:  "Describes a STACKIT Git instance by its internal ID.",
+		Args:  args.SingleArg(instanceIdArg, utils.ValidateUUID),
+		Example: examples.Build(
+			examples.NewExample(`Describe instance "xxx"`, `$ stackit git describe xxx`),
+		),
+		RunE: func(cmd *cobra.Command, args []string) error {
+			ctx := context.Background()
+			model, err := parseInput(params.Printer, cmd, args)
+			if err != nil {
+				return err
+			}
+
+			// Configure API client
+			apiClient, err := client.ConfigureClient(params.Printer)
+			if err != nil {
+				return err
+			}
+
+			// Call API
+			request := buildRequest(ctx, model, apiClient)
+
+			instance, err := request.Execute()
+			if err != nil {
+				return fmt.Errorf("get instance: %w", err)
+			}
+
+			if err := outputResult(params.Printer, model.OutputFormat, instance); err != nil {
+				return err
+			}
+
+			return nil
+		},
+	}
+
+	return cmd
+}
+
+func parseInput(p *print.Printer, cmd *cobra.Command, cliArgs []string) (*inputModel, error) {
+	globalFlags := globalflags.Parse(p, cmd)
+	if globalFlags.ProjectId == "" {
+		return nil, &errors.ProjectIdError{}
+	}
+
+	model := inputModel{
+		GlobalFlagModel: globalFlags,
+		InstanceId:      cliArgs[0],
+	}
+
+	if p.IsVerbosityDebug() {
+		modelStr, err := print.BuildDebugStrFromInputModel(model)
+		if err != nil {
+			p.Debug(print.ErrorLevel, "convert model to string for debugging: %v", err)
+		} else {
+			p.Debug(print.DebugLevel, "parsed input values: %s", modelStr)
+		}
+	}
+
+	return &model, nil
+}
+
+func buildRequest(ctx context.Context, model *inputModel, apiClient *git.APIClient) git.ApiGetInstanceRequest {
+	return apiClient.GetInstance(ctx, model.ProjectId, model.InstanceId)
+}
+
+func outputResult(p *print.Printer, outputFormat string, resp *git.Instance) error {
+	if resp == nil {
+		return fmt.Errorf("instance not found")
+	}
+	switch outputFormat {
+	case print.JSONOutputFormat:
+		details, err := json.MarshalIndent(resp, "", "  ")
+		if err != nil {
+			return fmt.Errorf("marshal instance: %w", err)
+		}
+		p.Outputln(string(details))
+
+		return nil
+	case print.YAMLOutputFormat:
+		details, err := yaml.MarshalWithOptions(resp, yaml.IndentSequence(true), yaml.UseJSONMarshaler())
+		if err != nil {
+			return fmt.Errorf("marshal instance: %w", err)
+		}
+		p.Outputln(string(details))
+
+		return nil
+	default:
+		table := tables.NewTable()
+		if id := resp.Id; id != nil {
+			table.AddRow("ID", *id)
+			table.AddSeparator()
+		}
+		if name := resp.Name; name != nil {
+			table.AddRow("NAME", *name)
+			table.AddSeparator()
+		}
+		if url := resp.Url; url != nil {
+			table.AddRow("URL", *url)
+			table.AddSeparator()
+		}
+		if version := resp.Version; version != nil {
+			table.AddRow("VERSION", *version)
+			table.AddSeparator()
+		}
+		if state := resp.State; state != nil {
+			table.AddRow("STATE", *state)
+			table.AddSeparator()
+		}
+		if created := resp.Created; created != nil {
+			table.AddRow("CREATED", *created)
+			table.AddSeparator()
+		}
+
+		if err := table.Display(p); err != nil {
+			return fmt.Errorf("render table: %w", err)
+		}
+
+		return nil
+	}
+}
diff --git a/internal/cmd/git/describe/describe_test.go b/internal/cmd/git/describe/describe_test.go
new file mode 100644
index 000000000..17cd8dae8
--- /dev/null
+++ b/internal/cmd/git/describe/describe_test.go
@@ -0,0 +1,226 @@
+package describe
+
+import (
+	"context"
+	"testing"
+
+	"github.com/google/go-cmp/cmp"
+	"github.com/google/go-cmp/cmp/cmpopts"
+	"github.com/google/uuid"
+	"github.com/stackitcloud/stackit-cli/internal/cmd/params"
+	"github.com/stackitcloud/stackit-cli/internal/pkg/globalflags"
+	"github.com/stackitcloud/stackit-cli/internal/pkg/print"
+	"github.com/stackitcloud/stackit-sdk-go/services/git"
+)
+
+type testCtxKey struct{}
+
+var (
+	testCtx        = context.WithValue(context.Background(), testCtxKey{}, "foo")
+	testClient     = &git.APIClient{}
+	testProjectId  = uuid.NewString()
+	testInstanceId = []string{uuid.NewString()}
+)
+
+func fixtureFlagValues(mods ...func(flagValues map[string]string)) map[string]string {
+	flagValues := map[string]string{
+		globalflags.ProjectIdFlag: testProjectId,
+	}
+	for _, mod := range mods {
+		mod(flagValues)
+	}
+	return flagValues
+}
+
+func fixtureInputModel(mods ...func(model *inputModel)) *inputModel {
+	model := &inputModel{
+		GlobalFlagModel: &globalflags.GlobalFlagModel{ProjectId: testProjectId, Verbosity: globalflags.VerbosityDefault},
+		InstanceId:      testInstanceId[0],
+	}
+	for _, mod := range mods {
+		mod(model)
+	}
+	return model
+}
+
+func fixtureRequest(mods ...func(request *git.ApiGetInstanceRequest)) git.ApiGetInstanceRequest {
+	request := testClient.GetInstance(testCtx, testProjectId, testInstanceId[0])
+	for _, mod := range mods {
+		mod(&request)
+	}
+	return request
+}
+
+func TestParseInput(t *testing.T) {
+	tests := []struct {
+		description   string
+		flagValues    map[string]string
+		isValid       bool
+		args          []string
+		expectedModel *inputModel
+	}{
+		{
+			description:   "base",
+			flagValues:    fixtureFlagValues(),
+			expectedModel: fixtureInputModel(),
+			args:          testInstanceId,
+			isValid:       true,
+		},
+		{
+			description: "no values",
+			flagValues:  map[string]string{},
+			args:        testInstanceId,
+			isValid:     false,
+		},
+		{
+			description: "project id missing",
+			flagValues: fixtureFlagValues(func(flagValues map[string]string) {
+				delete(flagValues, globalflags.ProjectIdFlag)
+			}),
+			args:    testInstanceId,
+			isValid: false,
+		},
+		{
+			description: "project id invalid 1",
+			flagValues: fixtureFlagValues(func(flagValues map[string]string) {
+				flagValues[globalflags.ProjectIdFlag] = ""
+			}),
+			args:    testInstanceId,
+			isValid: false,
+		},
+		{
+			description: "project id invalid 2",
+			flagValues: fixtureFlagValues(func(flagValues map[string]string) {
+				flagValues[globalflags.ProjectIdFlag] = "invalid-uuid"
+			}),
+			args:    testInstanceId,
+			isValid: false,
+		},
+		{
+			description: "no instance id passed",
+			flagValues:  fixtureFlagValues(),
+			args:        nil,
+			isValid:     false,
+		},
+		{
+			description: "multiple instance ids passed",
+			flagValues:  fixtureFlagValues(),
+			args:        []string{uuid.NewString(), uuid.NewString()},
+			isValid:     false,
+		},
+		{
+			description: "invalid instance id passed",
+			flagValues:  fixtureFlagValues(),
+			args:        []string{"foobar"},
+		},
+	}
+
+	for _, tt := range tests {
+		t.Run(tt.description, func(t *testing.T) {
+			p := print.NewPrinter()
+			cmd := NewCmd(&params.CmdParams{Printer: p})
+			if err := globalflags.Configure(cmd.Flags()); err != nil {
+				t.Errorf("cannot configure global flags: %v", err)
+			}
+			for flag, value := range tt.flagValues {
+				err := cmd.Flags().Set(flag, value)
+				if err != nil {
+					if !tt.isValid {
+						return
+					}
+					t.Fatalf("setting flag --%s=%s: %v", flag, value, err)
+				}
+			}
+
+			if err := cmd.ValidateRequiredFlags(); err != nil {
+				if !tt.isValid {
+					return
+				}
+				t.Fatalf("error validating flags: %v", err)
+			}
+
+			if err := cmd.ValidateArgs(tt.args); err != nil {
+				if !tt.isValid {
+					return
+				}
+			}
+
+			model, err := parseInput(p, cmd, tt.args)
+			if err != nil {
+				if !tt.isValid {
+					return
+				}
+				t.Fatalf("error parsing flags: %v", err)
+			}
+
+			if !tt.isValid {
+				t.Fatalf("did not fail on invalid input")
+			}
+			diff := cmp.Diff(model, tt.expectedModel)
+			if diff != "" {
+				t.Fatalf("Data does not match: %s", diff)
+			}
+		})
+	}
+}
+
+func TestBuildRequest(t *testing.T) {
+	tests := []struct {
+		description     string
+		model           *inputModel
+		expectedRequest git.ApiGetInstanceRequest
+	}{
+		{
+			description:     "base",
+			model:           fixtureInputModel(),
+			expectedRequest: fixtureRequest(),
+		},
+	}
+
+	for _, tt := range tests {
+		t.Run(tt.description, func(t *testing.T) {
+			request := buildRequest(testCtx, tt.model, testClient)
+			diff := cmp.Diff(request, tt.expectedRequest,
+				cmp.AllowUnexported(tt.expectedRequest),
+				cmpopts.EquateComparable(testCtx),
+			)
+			if diff != "" {
+				t.Fatalf("Data does not match: %s", diff)
+			}
+		})
+	}
+}
+
+func TestOutputResult(t *testing.T) {
+	type args struct {
+		outputFormat string
+		resp         *git.Instance
+	}
+	tests := []struct {
+		name    string
+		args    args
+		wantErr bool
+	}{
+		{
+			name: "empty",
+			args: args{
+				resp: &git.Instance{},
+			},
+			wantErr: false,
+		},
+		{
+			name:    "nil",
+			args:    args{},
+			wantErr: true,
+		},
+	}
+	p := print.NewPrinter()
+	p.Cmd = NewCmd(&params.CmdParams{Printer: p})
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			if err := outputResult(p, tt.args.outputFormat, tt.args.resp); (err != nil) != tt.wantErr {
+				t.Errorf("outputResult() error = %v, wantErr %v", err, tt.wantErr)
+			}
+		})
+	}
+}
diff --git a/internal/cmd/git/git.go b/internal/cmd/git/git.go
new file mode 100644
index 000000000..fcacb4fcd
--- /dev/null
+++ b/internal/cmd/git/git.go
@@ -0,0 +1,34 @@
+package git
+
+import (
+	"github.com/stackitcloud/stackit-cli/internal/cmd/git/create"
+	"github.com/stackitcloud/stackit-cli/internal/cmd/git/delete"
+	"github.com/stackitcloud/stackit-cli/internal/cmd/git/describe"
+	"github.com/stackitcloud/stackit-cli/internal/cmd/git/list"
+	"github.com/stackitcloud/stackit-cli/internal/cmd/params"
+	"github.com/stackitcloud/stackit-cli/internal/pkg/args"
+	"github.com/stackitcloud/stackit-cli/internal/pkg/utils"
+
+	"github.com/spf13/cobra"
+)
+
+func NewCmd(params *params.CmdParams) *cobra.Command {
+	cmd := &cobra.Command{
+		Use:   "git",
+		Short: "Provides functionality for STACKIT Git",
+		Long:  "Provides functionality for STACKIT Git.",
+		Args:  args.NoArgs,
+		Run:   utils.CmdHelp,
+	}
+	addSubcommands(cmd, params)
+	return cmd
+}
+
+func addSubcommands(cmd *cobra.Command, params *params.CmdParams) {
+	cmd.AddCommand(
+		list.NewCmd(params),
+		describe.NewCmd(params),
+		create.NewCmd(params),
+		delete.NewCmd(params),
+	)
+}
diff --git a/internal/cmd/git/list/list.go b/internal/cmd/git/list/list.go
new file mode 100644
index 000000000..0057342ff
--- /dev/null
+++ b/internal/cmd/git/list/list.go
@@ -0,0 +1,163 @@
+package list
+
+import (
+	"context"
+	"encoding/json"
+	"fmt"
+
+	"github.com/stackitcloud/stackit-cli/internal/cmd/params"
+
+	"github.com/goccy/go-yaml"
+	"github.com/spf13/cobra"
+	"github.com/stackitcloud/stackit-cli/internal/pkg/args"
+	"github.com/stackitcloud/stackit-cli/internal/pkg/errors"
+	"github.com/stackitcloud/stackit-cli/internal/pkg/examples"
+	"github.com/stackitcloud/stackit-cli/internal/pkg/flags"
+	"github.com/stackitcloud/stackit-cli/internal/pkg/globalflags"
+	"github.com/stackitcloud/stackit-cli/internal/pkg/print"
+	"github.com/stackitcloud/stackit-cli/internal/pkg/projectname"
+	"github.com/stackitcloud/stackit-cli/internal/pkg/services/git/client"
+	"github.com/stackitcloud/stackit-cli/internal/pkg/tables"
+	"github.com/stackitcloud/stackit-cli/internal/pkg/utils"
+	"github.com/stackitcloud/stackit-sdk-go/services/git"
+)
+
+type inputModel struct {
+	*globalflags.GlobalFlagModel
+	Limit *int64
+}
+
+const limitFlag = "limit"
+
+func NewCmd(params *params.CmdParams) *cobra.Command {
+	cmd := &cobra.Command{
+		Use:   "list",
+		Short: "Lists all instances of STACKIT Git.",
+		Long:  "Lists all instances of STACKIT Git for the current project.",
+		Args:  args.NoArgs,
+		Example: examples.Build(
+			examples.NewExample(
+				`List all STACKIT Git instances`,
+				"$ stackit git instance list"),
+			examples.NewExample(
+				"Lists up to 10 STACKIT Git instances",
+				"$ stackit git instance list --limit=10",
+			),
+		),
+		RunE: func(cmd *cobra.Command, _ []string) error {
+			ctx := context.Background()
+			model, err := parseInput(params.Printer, cmd)
+			if err != nil {
+				return err
+			}
+
+			// Configure API client
+			apiClient, err := client.ConfigureClient(params.Printer)
+			if err != nil {
+				return err
+			}
+
+			// Call API
+			req := buildRequest(ctx, model, apiClient)
+			resp, err := req.Execute()
+			if err != nil {
+				return fmt.Errorf("get STACKIT Git instances: %w", err)
+			}
+			instances := *resp.Instances
+			if len(instances) == 0 {
+				projectLabel, err := projectname.GetProjectName(ctx, params.Printer, params.CliVersion, cmd)
+				if err != nil {
+					params.Printer.Debug(print.ErrorLevel, "get project name: %v", err)
+					projectLabel = model.ProjectId
+				}
+				params.Printer.Info("No instances found for project %q\n", projectLabel)
+				return nil
+			} else if model.Limit != nil && len(instances) > int(*model.Limit) {
+				instances = (instances)[:*model.Limit]
+			}
+			return outputResult(params.Printer, model.OutputFormat, instances)
+		},
+	}
+	configureFlags(cmd)
+	return cmd
+}
+
+func configureFlags(cmd *cobra.Command) {
+	cmd.Flags().Int64(limitFlag, 0, "Limit the output to the first n elements")
+}
+
+func parseInput(p *print.Printer, cmd *cobra.Command) (*inputModel, error) {
+	globalFlags := globalflags.Parse(p, cmd)
+	if globalFlags.ProjectId == "" {
+		return nil, &errors.ProjectIdError{}
+	}
+
+	limit := flags.FlagToInt64Pointer(p, cmd, limitFlag)
+	if limit != nil && *limit < 1 {
+		return nil, &errors.FlagValidationError{
+			Flag:    limitFlag,
+			Details: "must be greater than 0",
+		}
+	}
+
+	model := inputModel{
+		GlobalFlagModel: globalFlags,
+		Limit:           limit,
+	}
+
+	if p.IsVerbosityDebug() {
+		modelStr, err := print.BuildDebugStrFromInputModel(model)
+		if err != nil {
+			p.Debug(print.ErrorLevel, "convert model to string for debugging: %v", err)
+		} else {
+			p.Debug(print.DebugLevel, "parsed input values: %s", modelStr)
+		}
+	}
+
+	return &model, nil
+}
+
+func buildRequest(ctx context.Context, model *inputModel, apiClient *git.APIClient) git.ApiListInstancesRequest {
+	return apiClient.ListInstances(ctx, model.ProjectId)
+}
+
+func outputResult(p *print.Printer, outputFormat string, instances []git.Instance) error {
+	switch outputFormat {
+	case print.JSONOutputFormat:
+		details, err := json.MarshalIndent(instances, "", "  ")
+		if err != nil {
+			return fmt.Errorf("marshal Observability instance list: %w", err)
+		}
+		p.Outputln(string(details))
+
+		return nil
+	case print.YAMLOutputFormat:
+		details, err := yaml.MarshalWithOptions(instances, yaml.IndentSequence(true), yaml.UseJSONMarshaler())
+		if err != nil {
+			return fmt.Errorf("marshal Observability instance list: %w", err)
+		}
+		p.Outputln(string(details))
+
+		return nil
+	default:
+		table := tables.NewTable()
+		table.SetHeader("ID", "NAME", "URL", "VERSION", "STATE", "CREATED")
+		for i := range instances {
+			instance := (instances)[i]
+			table.AddRow(
+				utils.PtrString(instance.Id),
+				utils.PtrString(instance.Name),
+				utils.PtrString(instance.Url),
+				utils.PtrString(instance.Version),
+				utils.PtrString(instance.State),
+				utils.PtrString(instance.Created),
+			)
+		}
+		err := table.Display(p)
+		if err != nil {
+			return fmt.Errorf("render table: %w", err)
+		}
+
+		return nil
+	}
+}
diff --git a/internal/cmd/git/list/list_test.go b/internal/cmd/git/list/list_test.go
new file mode 100644
index 000000000..f73297388
--- /dev/null
+++ b/internal/cmd/git/list/list_test.go
@@ -0,0 +1,237 @@
+package list
+
+import (
+	"context"
+	"strconv"
+	"testing"
+
+	"github.com/google/go-cmp/cmp"
+	"github.com/google/go-cmp/cmp/cmpopts"
+	"github.com/google/uuid"
+	"github.com/stackitcloud/stackit-cli/internal/cmd/params"
+	"github.com/stackitcloud/stackit-cli/internal/pkg/globalflags"
+	"github.com/stackitcloud/stackit-cli/internal/pkg/print"
+	"github.com/stackitcloud/stackit-cli/internal/pkg/utils"
+	"github.com/stackitcloud/stackit-sdk-go/services/git"
+)
+
+type testCtxKey struct{}
+
+var testCtx = context.WithValue(context.Background(), testCtxKey{}, "foo")
+var testClient = &git.APIClient{}
+var testProjectId = uuid.NewString()
+
+const (
+	testLimit = 10
+)
+
+func fixtureFlagValues(mods ...func(flagValues map[string]string)) map[string]string {
+	flagValues := map[string]string{
+		globalflags.ProjectIdFlag: testProjectId,
+	}
+	for _, mod := range mods {
+		mod(flagValues)
+	}
+	return flagValues
+}
+
+func fixtureInputModel(mods ...func(model *inputModel)) *inputModel {
+	model := &inputModel{
+		GlobalFlagModel: &globalflags.GlobalFlagModel{
+			ProjectId: testProjectId,
+			Verbosity: globalflags.VerbosityDefault,
+		},
+	}
+	for _, mod := range mods {
+		mod(model)
+	}
+	return model
+}
+
+func fixtureRequest(mods ...func(request *git.ApiListInstancesRequest)) git.ApiListInstancesRequest {
+	request := testClient.ListInstances(testCtx, testProjectId)
+	for _, mod := range mods {
+		mod(&request)
+	}
+	return request
+}
+
+func TestParseInput(t *testing.T) {
+	tests := []struct {
+		description   string
+		flagValues    map[string]string
+		isValid       bool
+		expectedModel *inputModel
+	}{
+		{
+			description:   "base",
+			flagValues:    fixtureFlagValues(),
+			isValid:       true,
+			expectedModel: fixtureInputModel(),
+		},
+		{
+			description: "no values",
+			flagValues:  map[string]string{},
+			isValid:     false,
+		},
+		{
+			description: "project id missing",
+			flagValues: fixtureFlagValues(func(flagValues map[string]string) {
+				delete(flagValues, globalflags.ProjectIdFlag)
+			}),
+			isValid: false,
+		},
+		{
+			description: "project id invalid 1",
+			flagValues: fixtureFlagValues(func(flagValues map[string]string) {
+				flagValues[globalflags.ProjectIdFlag] = ""
+			}),
+			isValid: false,
+		},
+		{
+			description: "project id invalid 2",
+			flagValues: fixtureFlagValues(func(flagValues map[string]string) {
+				flagValues[globalflags.ProjectIdFlag] = "invalid-uuid"
+			}),
+			isValid: false,
+		},
+		{
+			description: "with limit flag",
+			flagValues: fixtureFlagValues(func(flagValues map[string]string) {
+				flagValues["limit"] = strconv.Itoa(testLimit)
+			}),
+			isValid: true,
+			expectedModel: fixtureInputModel(func(model *inputModel) {
+				model.Limit = utils.Ptr(int64(testLimit))
+			}),
+		},
+		{
+			description: "with limit flag == 0",
+			flagValues: fixtureFlagValues(func(flagValues map[string]string) {
+				flagValues["limit"] = strconv.Itoa(0)
+			}),
+			isValid: false,
+		},
+		{
+			description: "with limit flag < 0",
+			flagValues: fixtureFlagValues(func(flagValues map[string]string) {
+				flagValues["limit"] = strconv.Itoa(-1)
+			}),
+			isValid: false,
+		},
+	}
+
+	for _, tt := range tests {
+		t.Run(tt.description, func(t *testing.T) {
+			p := print.NewPrinter()
+			cmd := NewCmd(&params.CmdParams{Printer: p})
+			err := globalflags.Configure(cmd.Flags())
+			if err != nil {
+				t.Fatalf("configure global flags: %v", err)
+			}
+
+			for flag, value := range tt.flagValues {
+				err := cmd.Flags().Set(flag, value)
+				if err != nil {
+					if !tt.isValid {
+						return
+					}
+					t.Fatalf("setting flag --%s=%s: %v", flag, value, err)
+				}
+			}
+
+			err = cmd.ValidateRequiredFlags()
+			if err != nil {
+				if !tt.isValid {
+					return
+				}
+				t.Fatalf("error validating flags: %v", err)
+			}
+
+			model, err := parseInput(p, cmd)
+			if err != nil {
+				if !tt.isValid {
+					return
+				}
+				t.Fatalf("error parsing flags: %v", err)
+			}
+
+			if !tt.isValid {
+				t.Fatalf("did not fail on invalid input")
+			}
+			diff := cmp.Diff(model, tt.expectedModel)
+			if diff != "" {
+				t.Fatalf("Data does not match: %s", diff)
+			}
+		})
+	}
+}
+
+func TestBuildRequest(t *testing.T) {
+	tests := []struct {
+		description     string
+		model           *inputModel
+		expectedRequest git.ApiListInstancesRequest
+	}{
+		{
+			description:     "base",
+			model:           fixtureInputModel(),
+			expectedRequest: fixtureRequest(),
+		},
+	}
+
+	for _, tt := range tests {
+		t.Run(tt.description, func(t *testing.T) {
+			request := buildRequest(testCtx, tt.model, testClient)
+
+			diff := cmp.Diff(request, tt.expectedRequest,
+				cmp.AllowUnexported(tt.expectedRequest),
+				cmpopts.EquateComparable(testCtx),
+			)
+			if diff != "" {
+				t.Fatalf("Data does not match: %s", diff)
+			}
+		})
+	}
+}
+
+func TestOutputResult(t *testing.T) {
+	type args struct {
+		outputFormat string
+		instances    []git.Instance
+	}
+	tests := []struct {
+		name    string
+		args    args
+		wantErr bool
+	}{
+		{
+			name:    "empty",
+			args:    args{},
+			wantErr: false,
+		},
+		{
+			name: "set empty instances slice",
+			args: args{
+				instances: []git.Instance{},
+			},
+			wantErr: false,
+		},
+		{
+			name: "set empty instances in instances slice",
+			args: args{
+				instances: []git.Instance{{}},
+			},
+			wantErr: false,
+		},
+	}
+	p := print.NewPrinter()
+	p.Cmd = NewCmd(&params.CmdParams{Printer: p})
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			if err := outputResult(p, tt.args.outputFormat, tt.args.instances); (err != nil) != tt.wantErr {
+				t.Errorf("outputResult() error = %v, wantErr %v", err, tt.wantErr)
+			}
+		})
+	}
+}
diff --git a/internal/cmd/root.go b/internal/cmd/root.go
index 6da6491d6..750bdfa9a 100644
--- a/internal/cmd/root.go
+++ b/internal/cmd/root.go
@@ -14,6 +14,7 @@ import (
 	configCmd "github.com/stackitcloud/stackit-cli/internal/cmd/config"
 	"github.com/stackitcloud/stackit-cli/internal/cmd/curl"
 	"github.com/stackitcloud/stackit-cli/internal/cmd/dns"
+	"github.com/stackitcloud/stackit-cli/internal/cmd/git"
 	"github.com/stackitcloud/stackit-cli/internal/cmd/image"
 	keypair "github.com/stackitcloud/stackit-cli/internal/cmd/key-pair"
 	loadbalancer "github.com/stackitcloud/stackit-cli/internal/cmd/load-balancer"
@@ -191,6 +192,7 @@ func addSubcommands(cmd *cobra.Command, params *params.CmdParams) {
 	cmd.AddCommand(image.NewCmd(params))
 	cmd.AddCommand(quota.NewCmd(params))
 	cmd.AddCommand(affinityGroups.NewCmd(params))
+	cmd.AddCommand(git.NewCmd(params))
 }
 
 // traverseCommands calls f for c and all of its children.
diff --git a/internal/pkg/config/config.go b/internal/pkg/config/config.go
index da282c335..957d7c475 100644
--- a/internal/pkg/config/config.go
+++ b/internal/pkg/config/config.go
@@ -45,6 +45,7 @@ const (
 	SQLServerFlexCustomEndpointKey     = "sqlserverflex_custom_endpoint"
 	IaaSCustomEndpointKey              = "iaas_custom_endpoint"
 	TokenCustomEndpointKey             = "token_custom_endpoint"
+	GitCustomEndpointKey               = "git_custom_endpoint"
 
 	ProjectNameKey     = "project_name"
 	DefaultProfileName = "default"
@@ -103,6 +104,7 @@ var ConfigKeys = []string{
 	SQLServerFlexCustomEndpointKey,
 	IaaSCustomEndpointKey,
 	TokenCustomEndpointKey,
+	GitCustomEndpointKey,
 }
 
 var defaultConfigFolderPath string
@@ -187,6 +189,7 @@ func setConfigDefaults() {
 	viper.SetDefault(SQLServerFlexCustomEndpointKey, "")
 	viper.SetDefault(IaaSCustomEndpointKey, "")
 	viper.SetDefault(TokenCustomEndpointKey, "")
+	viper.SetDefault(GitCustomEndpointKey, "")
 }
 
 func getConfigFilePath(configFolder string) string {
diff --git a/internal/pkg/services/git/client/client.go b/internal/pkg/services/git/client/client.go
new file mode 100644
index 000000000..dde3b7be6
--- /dev/null
+++ b/internal/pkg/services/git/client/client.go
@@ -0,0 +1,45 @@
+package client
+
+import (
+	"github.com/stackitcloud/stackit-cli/internal/pkg/auth"
+	"github.com/stackitcloud/stackit-cli/internal/pkg/config"
+	"github.com/stackitcloud/stackit-cli/internal/pkg/errors"
+	"github.com/stackitcloud/stackit-cli/internal/pkg/print"
+
+	"github.com/spf13/viper"
+	sdkConfig "github.com/stackitcloud/stackit-sdk-go/core/config"
+	"github.com/stackitcloud/stackit-sdk-go/services/git"
+)
+
+func ConfigureClient(p *print.Printer) (*git.APIClient, error) {
+	var err error
+	var apiClient *git.APIClient
+	var cfgOptions []sdkConfig.ConfigurationOption
+
+	authCfgOption, err := auth.AuthenticationConfig(p, auth.AuthorizeUser)
+	if err != nil {
+		p.Debug(print.ErrorLevel, "configure authentication: %v", err)
+		return nil, &errors.AuthError{}
+	}
+	cfgOptions = append(cfgOptions, authCfgOption)
+
+	customEndpoint := viper.GetString(config.GitCustomEndpointKey)
+
+	if customEndpoint != "" {
+		cfgOptions = append(cfgOptions, sdkConfig.WithEndpoint(customEndpoint))
+	}
+
+	if p.IsVerbosityDebug() {
+		cfgOptions = append(cfgOptions,
+			sdkConfig.WithMiddleware(print.RequestResponseCapturer(p, nil)),
+		)
+	}
+
+	apiClient, err = git.NewAPIClient(cfgOptions...)
+	if err != nil {
+		p.Debug(print.ErrorLevel, "create new API client: %v", err)
+		return nil, &errors.AuthError{}
+	}
+
+	return apiClient, nil
+}
diff --git a/internal/pkg/services/git/utils/utils.go b/internal/pkg/services/git/utils/utils.go
new file mode 100644
index 000000000..3a875c920
--- /dev/null
+++ b/internal/pkg/services/git/utils/utils.go
@@ -0,0 +1,23 @@
+package utils
+
+import (
+	"context"
+	"fmt"
+
+	"github.com/stackitcloud/stackit-sdk-go/services/git"
+)
+
+type GitClient interface {
+	GetInstanceExecute(ctx context.Context, projectId string, instanceId string) (*git.Instance, error)
+}
+
+func GetInstanceName(ctx context.Context, apiClient GitClient, projectId, instanceId string) (string, error) {
+	resp, err := apiClient.GetInstanceExecute(ctx, projectId, instanceId)
+	if err != nil {
+		return "", fmt.Errorf("get instance: %w", err)
+	}
+	if resp.Name == nil {
+		return "", nil
+	}
+	return *resp.Name, nil
+}
diff --git a/internal/pkg/services/git/utils/utils_test.go b/internal/pkg/services/git/utils/utils_test.go
new file mode 100644
index 000000000..7ec5dc494
--- /dev/null
+++ b/internal/pkg/services/git/utils/utils_test.go
@@ -0,0 +1,66 @@
+package utils
+
+import (
+	"context"
+	"fmt"
+	"testing"
+
+	"github.com/stackitcloud/stackit-cli/internal/pkg/utils"
+	"github.com/stackitcloud/stackit-sdk-go/services/git"
+)
+
+type GitClientMocked struct {
+	GetInstanceFails bool
+	GetInstanceResp  *git.Instance
+}
+
+func (m *GitClientMocked) GetInstanceExecute(_ context.Context, _, _ string) (*git.Instance, error) {
+	if m.GetInstanceFails {
+		return nil, fmt.Errorf("could not get instance")
+	}
+	return m.GetInstanceResp, nil
+}
+
+func TestGetinstanceName(t *testing.T) {
+	tests := []struct {
+		name         string
+		instanceResp *git.Instance
+		instanceErr  bool
+		want         string
+		wantErr      bool
+	}{
+		{
+			name:         "successful retrieval",
+			instanceResp: &git.Instance{Name: utils.Ptr("test-instance")},
+			want:         "test-instance",
+			wantErr:      false,
+		},
+		{
+			name:        "error on retrieval",
+			instanceErr: true,
+			wantErr:     true,
+		},
+		{
+			name:         "nil name",
+			instanceErr:  false,
+			instanceResp: &git.Instance{},
+			want:         "",
+		},
+	}
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			client := &GitClientMocked{
+				GetInstanceFails: tt.instanceErr,
+				GetInstanceResp:  tt.instanceResp,
+			}
+			got, err := GetInstanceName(context.Background(), client, "", "")
+			if (err != nil) != tt.wantErr {
+				t.Errorf("GetInstanceName() error = %v, wantErr %v", err, tt.wantErr)
+				return
+			}
+			if got != tt.want {
+				t.Errorf("GetInstanceName() = %v, want %v", got, tt.want)
+			}
+		})
+	}
+}


Reviews:
user: login: rubenhoenle
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-05-05T11:55:45Z
user: login: rubenhoenle
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-05-05T12:00:00Z
user: login: rubenhoenle
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-05-05T12:00:47Z
user: login: rubenhoenle
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-05-05T12:01:13Z
user: login: bahkauv70
body: Added some minor stuff. When this is fixed, the PR may be merged
state: CHANGES_REQUESTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-08T10:03:47Z
user: login: bahkauv70
body: Looks good now, only the pipeline fails. The reason could be, that _snyk_ is not able to handle the toolchain option from go.mod. Could you please remove this and try again? If the pipeline is green, we can merge this
state: CHANGES_REQUESTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-13T08:06:33Z
user: login: bahkauv70
state: APPROVED
author_association: CONTRIBUTOR
submitted_at: 2025-05-13T11:03:13Z

Review comments:
diff_hunk: @@ -0,0 +1,208 @@
+package list
+
+import (
+	"context"
+	"testing"
+
+	"github.com/google/go-cmp/cmp"
+	"github.com/google/go-cmp/cmp/cmpopts"
+	"github.com/google/uuid"
+	"github.com/stackitcloud/stackit-cli/internal/pkg/globalflags"
+	"github.com/stackitcloud/stackit-cli/internal/pkg/print"
+	"github.com/stackitcloud/stackit-sdk-go/services/git"
+)
+
+var projectIdFlag = globalflags.ProjectIdFlag
path: internal/cmd/git/list/list_test.go
user: login: rubenhoenle
body: I know that you find this in some spots across our codebase, but we want to get rid of introducing new variables for these global flags.

So I kindly ask you to apply the following change: Instead of `projectIdFlag` just directly use `globalflags.ProjectIdFlag` please and get rid of that variable.

Many thanks :smile: 
created_at: 2025-05-05T11:55:45Z
author_association: MEMBER
diff_hunk: @@ -0,0 +1,139 @@
+package list
+
+import (
+	"context"
+	"encoding/json"
+	"fmt"
+
+	"github.com/goccy/go-yaml"
+	"github.com/spf13/cobra"
+	"github.com/stackitcloud/stackit-cli/internal/pkg/args"
+	"github.com/stackitcloud/stackit-cli/internal/pkg/errors"
+	"github.com/stackitcloud/stackit-cli/internal/pkg/examples"
+	"github.com/stackitcloud/stackit-cli/internal/pkg/globalflags"
+	"github.com/stackitcloud/stackit-cli/internal/pkg/print"
+	"github.com/stackitcloud/stackit-cli/internal/pkg/projectname"
+	"github.com/stackitcloud/stackit-cli/internal/pkg/services/git/client"
+	"github.com/stackitcloud/stackit-cli/internal/pkg/tables"
+	"github.com/stackitcloud/stackit-cli/internal/pkg/utils"
+	"github.com/stackitcloud/stackit-sdk-go/services/git"
+)
+
+type inputModel struct {
+	*globalflags.GlobalFlagModel
+}
+
+func NewCmd(p *print.Printer) *cobra.Command {
+	cmd := &cobra.Command{
+		Use:   "list",
+		Short: "Lists all instances of STACKIT Git.",
+		Long:  "Lists all instances of STACKIT Git current the current project.",
path: internal/cmd/git/list/list.go
user: login: rubenhoenle
body: Please re-check this long description, something went wrong there ^^
created_at: 2025-05-05T12:00:00Z
author_association: MEMBER
diff_hunk: @@ -0,0 +1,183 @@
+package delete
+
+import (
+	"context"
+	"testing"
+
+	"github.com/stackitcloud/stackit-cli/internal/pkg/globalflags"
+	"github.com/stackitcloud/stackit-cli/internal/pkg/print"
+
+	"github.com/google/go-cmp/cmp"
+	"github.com/google/go-cmp/cmp/cmpopts"
+	"github.com/google/uuid"
+	"github.com/stackitcloud/stackit-sdk-go/services/git"
+)
+
+var projectIdFlag = globalflags.ProjectIdFlag
path: internal/cmd/git/delete/delete_test.go
user: login: rubenhoenle
body: ```suggestion

```

same here with the variable
created_at: 2025-05-05T12:00:47Z
author_association: MEMBER
diff_hunk: @@ -0,0 +1,258 @@
+package create
+
+import (
+	"context"
+	"testing"
+
+	"github.com/google/go-cmp/cmp"
+	"github.com/google/go-cmp/cmp/cmpopts"
+	"github.com/google/uuid"
+	"github.com/stackitcloud/stackit-cli/internal/pkg/globalflags"
+	"github.com/stackitcloud/stackit-cli/internal/pkg/print"
+	"github.com/stackitcloud/stackit-cli/internal/pkg/utils"
+	"github.com/stackitcloud/stackit-sdk-go/services/git"
+)
+
+var projectIdFlag = globalflags.ProjectIdFlag
path: internal/cmd/git/create/create_test.go
user: login: rubenhoenle
body: ```suggestion

```

same here with the variable
created_at: 2025-05-05T12:01:13Z
author_association: MEMBER
diff_hunk: @@ -0,0 +1,165 @@
+package create
+
+import (
+	"context"
+	"encoding/json"
+	"fmt"
+
+	"github.com/goccy/go-yaml"
+	"github.com/spf13/cobra"
+	"github.com/stackitcloud/stackit-cli/internal/pkg/args"
+	"github.com/stackitcloud/stackit-cli/internal/pkg/errors"
+	"github.com/stackitcloud/stackit-cli/internal/pkg/examples"
+	"github.com/stackitcloud/stackit-cli/internal/pkg/flags"
+	"github.com/stackitcloud/stackit-cli/internal/pkg/globalflags"
+	"github.com/stackitcloud/stackit-cli/internal/pkg/print"
+	"github.com/stackitcloud/stackit-cli/internal/pkg/services/git/client"
+	"github.com/stackitcloud/stackit-cli/internal/pkg/spinner"
+	"github.com/stackitcloud/stackit-cli/internal/pkg/utils"
+	"github.com/stackitcloud/stackit-sdk-go/services/git"
+	"github.com/stackitcloud/stackit-sdk-go/services/git/wait"
+)
+
+const (
+	nameFlag = "name"
+)
+
+type inputModel struct {
+	*globalflags.GlobalFlagModel
+	Id   *string
+	Name string
+}
+
+func NewCmd(p *print.Printer) *cobra.Command {
+	cmd := &cobra.Command{
+		Use:   "create",
+		Short: "Creates STACKIT Git instance",
+		Long:  "Create an STACKIT Git instance by name.",
path: internal/cmd/git/create/create.go
user: login: bahkauv70
body: typo
```suggestion
		Long:  "Create a STACKIT Git instance by name.",
```
created_at: 2025-05-08T09:36:10Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,130 @@
+package delete
+
+import (
+	"context"
+	"fmt"
+
+	"github.com/spf13/cobra"
+	"github.com/stackitcloud/stackit-cli/internal/pkg/args"
+	"github.com/stackitcloud/stackit-cli/internal/pkg/errors"
+	"github.com/stackitcloud/stackit-cli/internal/pkg/examples"
+	"github.com/stackitcloud/stackit-cli/internal/pkg/globalflags"
+	"github.com/stackitcloud/stackit-cli/internal/pkg/print"
+	"github.com/stackitcloud/stackit-cli/internal/pkg/projectname"
+	"github.com/stackitcloud/stackit-cli/internal/pkg/services/git/client"
+	gitUtils "github.com/stackitcloud/stackit-cli/internal/pkg/services/git/utils"
+	"github.com/stackitcloud/stackit-cli/internal/pkg/spinner"
+	"github.com/stackitcloud/stackit-cli/internal/pkg/utils"
+	"github.com/stackitcloud/stackit-sdk-go/services/git"
+	"github.com/stackitcloud/stackit-sdk-go/services/git/wait"
+)
+
+type inputModel struct {
+	*globalflags.GlobalFlagModel
+	InstanceId string
+}
+
+const instanceIdArg = "INSTANCE_ID"
+
+func NewCmd(p *print.Printer) *cobra.Command {
+	cmd := &cobra.Command{
+		Use:   fmt.Sprintf("delete %s", instanceIdArg),
+		Short: "Deletes STACKIT Git instance",
+		Long:  "Deletes an STACKIT Git instance by its internal ID.",
path: internal/cmd/git/delete/delete.go
user: login: bahkauv70
body: typo
```suggestion
		Long:  "Deletes a STACKIT Git instance by its internal ID.",
```
created_at: 2025-05-08T09:37:31Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,135 @@
+package describe
+
+import (
+	"context"
+	"encoding/json"
+	"fmt"
+
+	"github.com/goccy/go-yaml"
+	"github.com/spf13/cobra"
+	"github.com/stackitcloud/stackit-cli/internal/pkg/args"
+	"github.com/stackitcloud/stackit-cli/internal/pkg/errors"
+	"github.com/stackitcloud/stackit-cli/internal/pkg/examples"
+	"github.com/stackitcloud/stackit-cli/internal/pkg/globalflags"
+	"github.com/stackitcloud/stackit-cli/internal/pkg/print"
+	"github.com/stackitcloud/stackit-cli/internal/pkg/services/git/client"
+	"github.com/stackitcloud/stackit-cli/internal/pkg/tables"
+	"github.com/stackitcloud/stackit-cli/internal/pkg/utils"
+	"github.com/stackitcloud/stackit-sdk-go/services/git"
+)
+
+type inputModel struct {
+	*globalflags.GlobalFlagModel
+	InstanceId string
+}
+
+const instanceIdArg = "INSTANCE_ID"
+
+func NewCmd(p *print.Printer) *cobra.Command {
+	cmd := &cobra.Command{
+		Use:   fmt.Sprintf("describe %s", instanceIdArg),
+		Short: "Describes STACKIT Git instance",
+		Long:  "Describes an STACKIT Git instance by its internal ID.",
path: internal/cmd/git/describe/describe.go
user: login: bahkauv70
body: typo

```suggestion
		Long:  "Describes a STACKIT Git instance by its internal ID.",
```
created_at: 2025-05-08T09:40:57Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,139 @@
+package list
+
+import (
+	"context"
+	"encoding/json"
+	"fmt"
+
+	"github.com/goccy/go-yaml"
+	"github.com/spf13/cobra"
+	"github.com/stackitcloud/stackit-cli/internal/pkg/args"
+	"github.com/stackitcloud/stackit-cli/internal/pkg/errors"
+	"github.com/stackitcloud/stackit-cli/internal/pkg/examples"
+	"github.com/stackitcloud/stackit-cli/internal/pkg/globalflags"
+	"github.com/stackitcloud/stackit-cli/internal/pkg/print"
+	"github.com/stackitcloud/stackit-cli/internal/pkg/projectname"
+	"github.com/stackitcloud/stackit-cli/internal/pkg/services/git/client"
+	"github.com/stackitcloud/stackit-cli/internal/pkg/tables"
+	"github.com/stackitcloud/stackit-cli/internal/pkg/utils"
+	"github.com/stackitcloud/stackit-sdk-go/services/git"
+)
+
+type inputModel struct {
+	*globalflags.GlobalFlagModel
path: internal/cmd/git/list/list.go
user: login: bahkauv70
body: commonly we also implement a limit for the number of listed instances. See [here](https://github.com/peremanent/stackit-cli/blob/61b9c73eb4866d308a3670eae6d1476cafc699eb/internal/cmd/affinity-groups/list/list.go#L25) for an example
created_at: 2025-05-08T09:45:23Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,135 @@
+package describe
+
+import (
+	"context"
+	"encoding/json"
+	"fmt"
+
+	"github.com/goccy/go-yaml"
+	"github.com/spf13/cobra"
+	"github.com/stackitcloud/stackit-cli/internal/pkg/args"
+	"github.com/stackitcloud/stackit-cli/internal/pkg/errors"
+	"github.com/stackitcloud/stackit-cli/internal/pkg/examples"
+	"github.com/stackitcloud/stackit-cli/internal/pkg/globalflags"
+	"github.com/stackitcloud/stackit-cli/internal/pkg/print"
+	"github.com/stackitcloud/stackit-cli/internal/pkg/services/git/client"
+	"github.com/stackitcloud/stackit-cli/internal/pkg/tables"
+	"github.com/stackitcloud/stackit-cli/internal/pkg/utils"
+	"github.com/stackitcloud/stackit-sdk-go/services/git"
+)
+
+type inputModel struct {
+	*globalflags.GlobalFlagModel
+	InstanceId string
+}
+
+const instanceIdArg = "INSTANCE_ID"
+
+func NewCmd(p *print.Printer) *cobra.Command {
+	cmd := &cobra.Command{
+		Use:   fmt.Sprintf("describe %s", instanceIdArg),
+		Short: "Describes STACKIT Git instance",
+		Long:  "Describes an STACKIT Git instance by its internal ID.",
+		Args:  args.SingleArg(instanceIdArg, utils.ValidateUUID),
+		Example: examples.Build(
+			examples.NewExample(`Describe instance "xxx"`, `$ stackit git describe xxx`),
+		),
+		RunE: func(cmd *cobra.Command, args []string) error {
+			ctx := context.Background()
+			model, err := parseInput(p, cmd, args)
+			if err != nil {
+				return err
+			}
+
+			// Configure API client
+			apiClient, err := client.ConfigureClient(p)
+			if err != nil {
+				return err
+			}
+
+			// Call API
+			request := buildRequest(ctx, model, apiClient)
+
+			instance, err := request.Execute()
+			if err != nil {
+				return fmt.Errorf("get instance: %w", err)
+			}
+
+			if err := outputResult(p, model.OutputFormat, instance); err != nil {
+				return err
+			}
+
+			return nil
+		},
+	}
+
+	return cmd
+}
+
+func parseInput(p *print.Printer, cmd *cobra.Command, cliArgs []string) (*inputModel, error) {
+	globalFlags := globalflags.Parse(p, cmd)
+	if globalFlags.ProjectId == "" {
+		return nil, &errors.ProjectIdError{}
+	}
+
+	model := inputModel{
+		GlobalFlagModel: globalFlags,
+		InstanceId:      cliArgs[0],
+	}
+
+	if p.IsVerbosityDebug() {
+		modelStr, err := print.BuildDebugStrFromInputModel(model)
+		if err != nil {
+			p.Debug(print.ErrorLevel, "convert model to string for debugging: %v", err)
+		} else {
+			p.Debug(print.DebugLevel, "parsed input values: %s", modelStr)
+		}
+	}
+
+	return &model, nil
+}
+
+func buildRequest(ctx context.Context, model *inputModel, apiClient *git.APIClient) git.ApiGetInstanceRequest {
+	return apiClient.GetInstance(ctx, model.ProjectId, model.InstanceId)
+}
+
+func outputResult(p *print.Printer, outputFormat string, resp *git.Instance) error {
+	if resp == nil {
+		return fmt.Errorf("instance not found")
+	}
+	switch outputFormat {
+	case print.JSONOutputFormat:
+		details, err := json.MarshalIndent(resp, "", "  ")
+		if err != nil {
+			return fmt.Errorf("marshal instance: %w", err)
+		}
+		p.Outputln(string(details))
+
+		return nil
+	case print.YAMLOutputFormat:
+		details, err := yaml.MarshalWithOptions(resp, yaml.IndentSequence(true), yaml.UseJSONMarshaler())
+		if err != nil {
+			return fmt.Errorf("marshal instance: %w", err)
+		}
+		p.Outputln(string(details))
+
+		return nil
+	default:
+		table := tables.NewTable()
path: internal/cmd/git/describe/describe.go
user: login: bahkauv70
body: for the describe command we commonly use a vertical layout, in contrast to the horizontal used for the list commands. See `stackit image describe 48c56583-2084-406a-98c7-e652d1f87279` for an example
created_at: 2025-05-08T09:57:29Z
author_association: CONTRIBUTOR

PR comments:
user: login: rubenhoenle
created_at: 2025-05-05T12:02:38Z
body: @peremanent FYI: The CI run failed because docs aren't up to-to-date. Please re-generate the docs with `make generate-docs`
author_association: MEMBER
user: login: bahkauv70
created_at: 2025-05-13T08:57:00Z
body: synk still has an issue, we'll check this again
author_association: CONTRIBUTOR