diff --git a/cmd/tk/create_source.go b/cmd/tk/create_source.go
index 1f4ae8dd60374841cd6555dc716da5b2601204e3..2fe5e1943c31ce898395a49cb53bd245ec167c27 100644
--- a/cmd/tk/create_source.go
+++ b/cmd/tk/create_source.go
@@ -22,10 +22,11 @@ import (
 
 var createSourceCmd = &cobra.Command{
 	Use:   "source [name]",
-	Short: "Create source resource",
+	Short: "Create or update a source resource",
 	Long: `
 The create source command generates a source.fluxcd.io resource and waits for it to sync.
-For Git over SSH, host and SSH keys are automatically generated.`,
+For Git over SSH, host and SSH keys are automatically generated and stored in a Kubernetes secret.
+For private Git repositories, the basic authentication credentials are stored in a Kubernetes secret.`,
 	Example: `  # Create a source from a public Git repository master branch
   create source podinfo --git-url https://github.com/stefanprodan/podinfo-deploy --git-branch master
 
@@ -131,20 +132,8 @@ func createSourceCmdRun(cmd *cobra.Command, args []string) error {
 		return err
 	}
 
-	namespacedName := types.NamespacedName{
-		Namespace: namespace,
-		Name:      name,
-	}
-
-	err = kubeClient.Get(ctx, namespacedName, &gitRepository)
-	if errors.IsNotFound(err) {
-		if err := kubeClient.Create(ctx, &gitRepository); err != nil {
-			return err
-		}
-	} else {
-		if err := kubeClient.Update(ctx, &gitRepository); err != nil {
-			return err
-		}
+	if err := upsertGitRepository(ctx, kubeClient, gitRepository); err != nil {
+		return err
 	}
 
 	logAction("waiting for source sync")
@@ -155,13 +144,17 @@ func createSourceCmdRun(cmd *cobra.Command, args []string) error {
 
 	logSuccess("source %s is ready", name)
 
+	namespacedName := types.NamespacedName{
+		Namespace: namespace,
+		Name:      name,
+	}
 	err = kubeClient.Get(ctx, namespacedName, &gitRepository)
 	if err != nil {
 		return fmt.Errorf("source sync failed: %w", err)
 	}
 
 	if gitRepository.Status.Artifact != nil {
-		logSuccess("revision %s", gitRepository.Status.Artifact.Revision)
+		logSuccess("fetched revision %s", gitRepository.Status.Artifact.Revision)
 	} else {
 		return fmt.Errorf("source sync failed, artifact not found")
 	}
@@ -222,6 +215,35 @@ func generateSSH(ctx context.Context, name, host, tmpDir string) error {
 	return nil
 }
 
+func upsertGitRepository(ctx context.Context, kubeClient client.Client, gitRepository sourcev1.GitRepository) error {
+	namespacedName := types.NamespacedName{
+		Namespace: gitRepository.GetNamespace(),
+		Name:      gitRepository.GetName(),
+	}
+
+	var existing sourcev1.GitRepository
+	err := kubeClient.Get(ctx, namespacedName, &existing)
+	if err != nil {
+		if errors.IsNotFound(err) {
+			if err := kubeClient.Create(ctx, &gitRepository); err != nil {
+				return err
+			} else {
+				logSuccess("source created")
+				return nil
+			}
+		}
+		return err
+	}
+
+	existing.Spec = gitRepository.Spec
+	if err := kubeClient.Update(ctx, &existing); err != nil {
+		return err
+	}
+
+	logSuccess("source updated")
+	return nil
+}
+
 func isGitRepositoryReady(ctx context.Context, kubeClient client.Client, name, namespace string) wait.ConditionFunc {
 	return func() (bool, error) {
 		var gitRepository sourcev1.GitRepository