Showing posts with label GitHub. Show all posts
Showing posts with label GitHub. Show all posts

Thursday, September 10, 2026

Git Checkout vs. Git Switch: Comparing and Contrasting Two Branch Commands

See All Posts on GitHub    « Previously

Git Checkout vs. Git Switch: Comparing and Contrasting Two Branch Commands

Git provides several commands for working with branches, but two commands that often cause confusion are git checkout and git switch. Both can be used to move from one branch to another, but they are not equivalent in purpose or design. Understanding the difference helps you write clearer Git commands and avoid accidentally performing the wrong operation.

1. Why Are There Two Commands?

Historically, git checkout was the main command used to change branches. However, it eventually became a multi-purpose command that could do several unrelated things, including switching branches and restoring files.

This created a common source of confusion. For example, the same command could mean "move to another branch" in one situation and "restore this file" in another.

To make these operations easier to understand, Git introduced git switch and git restore. The idea was to separate the responsibilities that had previously been handled by git checkout.

The key idea: git switch is specifically designed for changing branches, while git checkout is a broader, older command that can change branches as well as check out files and specific commits.

2. Using Git Checkout to Switch Branches

Before git switch existed, developers commonly used git checkout to move from one branch to another.

git checkout feature-login

This tells Git to move the current working directory from the current branch to feature-login.

For example, suppose you are currently on main:

git branch

* main
  feature-login
  bugfix

Running:

git checkout feature-login

changes the current branch to feature-login.

3. Using Git Switch to Change Branches

The same operation can be expressed more explicitly with git switch:

git switch feature-login

The meaning is immediately clear: switch the current branch to feature-login.

This is one of the main reasons git switch was introduced. Its purpose is narrower and therefore easier to understand.

4. Comparing the Basic Syntax

Operation Git Checkout Git Switch
Switch to an existing branch git checkout feature git switch feature
Create and switch to a new branch git checkout -b feature git switch -c feature
Switch to another commit git checkout abc123 Not its primary purpose
Restore a file git checkout -- file.txt Not supported for this purpose

5. Creating a New Branch

Both commands can create a new branch and immediately switch to it.

Using git checkout

git checkout -b feature-payment

The -b option tells checkout to create the branch and then move to it.

Using git switch

git switch -c feature-payment

Here, -c means create a new branch and switch to it.

The result is essentially the same: a new branch named feature-payment is created and becomes the current branch.

6. The Important Difference: Checkout Does More

The biggest difference between the two commands is not what they can do when switching branches, but what checkout can do beyond branch switching.

Historically, git checkout has been used for several different tasks:

  • Switching between branches.
  • Creating and switching to a new branch.
  • Checking out a particular commit.
  • Restoring files from another commit or branch.

By contrast, git switch focuses on branches. It does not replace every capability of git checkout.

7. Checkout Can Move to a Specific Commit

One common use of git checkout is moving to a particular commit rather than a branch.

git checkout abc123

This places Git at the specified commit. Because that commit is not necessarily the tip of a branch, you may end up in what Git calls a detached HEAD state.

git switch is intentionally focused on branch switching, so checking out an arbitrary commit is not its primary role.

Important: Being in a detached HEAD state is not inherently an error. It is useful when you want to inspect or experiment with a particular commit without moving an existing branch pointer. However, commits created there can become difficult to find if you do not create a branch to keep them.

8. Checkout Was Also Used to Restore Files

Another source of confusion was that git checkout could operate on files. For example:

git checkout -- README.md

Historically, this was a way to replace the working copy of README.md with the version from the current commit.

Modern Git provides git restore specifically for file restoration:

git restore README.md

This separation makes Git's commands easier to understand:

git switch   → work with branches
git restore  → restore files
git checkout → older, multi-purpose command

9. Switching to a Remote Branch

git switch also provides a convenient way to create a local branch that tracks a remote branch.

For example:

git switch --track origin/feature-login

This creates a local branch associated with the remote-tracking branch and switches to it.

With modern Git versions, Git can often infer the remote branch when the local branch does not yet exist:

git switch feature-login

If Git finds an appropriate remote-tracking branch, it can create the local branch and establish the tracking relationship.

10. Why Git Switch Is Easier to Understand

Consider the following two commands:

git checkout feature-login
git switch feature-login

Both can switch branches, but the second command communicates its intention more explicitly. When you see git switch, you immediately know that the operation concerns branches.

This is particularly useful for beginners because it reduces the number of unrelated meanings they need to remember for one command.

11. Are They Completely Interchangeable?

No. They overlap when the task is switching branches, but they are not interchangeable in every situation.

Capability git checkout git switch
Switch branches Yes Yes
Create and switch to a branch Yes, with -b Yes, with -c
Track a remote branch Yes Yes
Move directly to a commit Yes Not its intended role
Restore files Historically yes No
Dedicated to branch operations No Yes

12. Which Command Should You Use?

For everyday branch operations, git switch is generally the clearer choice in modern Git.

For example, prefer:

git switch main
git switch feature-login
git switch -c feature-payment

rather than relying on git checkout for every branch-related operation.

However, you should still understand git checkout. You will encounter it frequently in older documentation, scripts, tutorials, Stack Overflow answers, and existing projects.

13. A Simple Mental Model

A useful way to remember the difference is to think about the verbs represented by the commands:

git switch → "I want to switch branches."

git restore → "I want to restore files."

git checkout → "I am using the older, multi-purpose command that historically handled both kinds of operations."

14. Final Comparison

git checkout and git switch overlap significantly when it comes to changing branches. If you run:

git checkout develop

or:

git switch develop

the intended result is the same: your working tree moves to the develop branch.

The difference is that git checkout is a broader, historically overloaded command, while git switch was introduced to make branch switching a distinct and easier-to-understand operation.

Therefore, a good modern rule of thumb is: use git switch when your intention is to work with branches, and learn git checkout so you can understand existing Git commands and older workflows.


See All Posts on GitHub    « Previously

Fast-Forwarding Merge vs. Non-Fast-Forwarding Merge in Git

See All Posts on GitHub    « Previously    Next »

Fast-Forwarding Merge vs. Non-Fast-Forwarding Merge in Git

When you merge branches in Git, Git has to decide how to combine the histories of those branches. Sometimes it can simply move a branch pointer forward. Other times, it must create a new commit to record the merge.

The key difference: A fast-forward merge does not create a new merge commit. A non-fast-forward merge creates a new merge commit that explicitly joins two lines of development.

First, Understand What a Git Branch Really Is

Before understanding the two types of merges, it helps to remember that a Git branch is essentially a movable pointer to a commit.

Suppose you start with a main branch:

A --- B --- C
              ^
             main

You then create a branch called feature from commit C and make two commits:

A --- B --- C --- D --- E
              ^           ^
             main       feature

Notice something important: main is still pointing to C, while feature is ahead of it.

This particular situation is what makes a fast-forward merge possible.

What Is a Fast-Forward Merge?

A fast-forward merge happens when the branch you are merging into has not moved forward since the feature branch was created. In other words, the target branch is an ancestor of the branch being merged.

Git does not need to create a new commit because there are no competing changes to combine. It can simply move the target branch pointer forward.

Before the Merge

A --- B --- C --- D --- E
              ^           ^
             main       feature

If you run:

git switch main
git merge feature

Git can simply move main from C to E.

After the Merge

A --- B --- C --- D --- E
                          ^
                    main, feature

No new commit was created. The history is still a straight line.

Think of it this way: Git is saying, "There is nothing to merge. The target branch is simply behind. I can move its pointer forward to the latest commit."

Why Is It Called "Fast-Forward"?

The term comes from what Git does to the branch pointer. It does not perform a complicated three-way merge or create a new commit. It simply fast-forwards the branch reference to a later commit.

Imagine that main is pointing at page 3 of a book and feature has continued the same story through page 5. There is no second story to reconcile. Git can simply move the bookmark from page 3 to page 5.

What Is a Non-Fast-Forwarding Merge?

A non-fast-forward merge is required when both branches have developed independently.

For example, suppose main contains commit D, while the feature branch was created earlier from commit C and contains commits F and G:

              D --- E
             /
A --- B --- C
             \
              F --- G
                    ^
                  feature

Here, main and feature have diverged. The feature branch cannot simply replace main's pointer, because main contains commits that are not part of the feature branch.

Git therefore needs to combine the two histories.

The Merge Commit

When you run:

git switch main
git merge feature

Git can create a new merge commit:

              D --- E
             /         \
A --- B --- C           M
             \         /
              F --- G
                    ^
                  feature

The new commit M has two parents: one from the existing main history and one from the feature history. It records the point at which the two lines of development were brought together.

Think of it this way: Git is saying, "These branches have both moved forward independently, so I need a new commit that joins their histories."

A Simple Real-World Example

Imagine you are working on a website.

You create a login-page branch from main and work on the login page:

git switch main
git switch -c login-page

You make several commits:

git commit -m "Add login form"
git commit -m "Add login validation"

Meanwhile, nobody makes any new commits to main.

The history looks like this:

main
  |
  v
A --- B --- C --- D
              ^
          login-page

Merging login-page into main can be a fast-forward:

A --- B --- C --- D
                  ^
             main, login-page

Now consider a different situation. While you were working on the login page, another developer added commits to main:

              X --- Y
             /
A --- B --- C
             \
              D --- E
                    ^
                login-page

Now the branches have diverged. Git cannot simply move main forward to E, because that would leave commits X and Y out of the resulting history.

A merge commit can join both histories:

              X --- Y
             /         \
A --- B --- C           M
             \         /
              D --- E
                    ^
                login-page

Fast-Forward vs. Non-Fast-Forward

Characteristic Fast-Forward Merge Non-Fast-Forward Merge
Branches diverged? No Yes
Creates a merge commit? No Usually yes
History shape Straight line Branches join together
What Git does Moves the branch pointer forward Creates a commit combining the histories
Useful for Simple linear development Preserving explicit branch/merge history

Forcing a Non-Fast-Forward Merge

There is an important Git option that lets you create a merge commit even when a fast-forward merge would be possible.

git merge --no-ff feature

The --no-ff option means "do not fast-forward."

Suppose the history is:

A --- B --- C --- D
              ^       ^
             main   feature

A normal merge could simply move main to D. But with:

git merge --no-ff feature

Git creates a merge commit:

A --- B --- C --- D
              \       /
                --- M
                    ^
                   main

The exact visual layout of the graph depends on the history, but the important point is that M is an explicit merge commit.

Why Would You Use --no-ff?

At first, creating an extra commit may seem unnecessary. However, it can make the history easier to understand.

Suppose every feature branch is merged with --no-ff. The history can show clearly that a group of commits represented one feature:

--- A --- B ----------- M1 --- N -------- M2 --->
          \             /              /
           C --- D --- /              /
                                      /
                       E --- F -------

The merge commits provide visible boundaries between pieces of work. This can be particularly useful in team environments where understanding the development history matters.

Important: --no-ff does not mean that Git will ignore the actual changes from the branch. It simply tells Git to preserve the merge as an explicit commit instead of reducing it to a pointer movement.

Fast-Forward Is Not the Same as "No Merge"

This is a common source of confusion.

When Git performs a fast-forward, people sometimes say that "nothing was merged." Technically, the changes from the feature branch become part of the target branch, but Git does not need to create a merge commit.

The important distinction is between integrating the changes and creating a merge commit.

A fast-forward integrates the branch by moving the target branch pointer. A non-fast-forward merge integrates the histories by creating a new commit with both histories as parents.

How Git Decides Which One to Use

When you run:

git merge feature

Git examines the relationship between the current branch and feature.

  • If the current branch is an ancestor of feature, Git can fast-forward.
  • If the branches have diverged, Git needs to perform a real merge and create a merge commit, assuming the merge is successful.
  • If you use --no-ff, Git creates a merge commit even when fast-forwarding would otherwise be possible.

What About Conflicts?

A non-fast-forward merge may result in merge conflicts when Git cannot automatically determine how to combine changes made on the two branches.

For example, if both branches modify the same lines of the same file, Git may stop and ask you to resolve the conflict.

Fast-forward merges generally do not encounter this type of merge conflict because there is no divergent history to reconcile. Git is simply advancing the branch pointer along an existing line of commits.

A Useful Mental Model

Fast-forward

Think of two people reading the same book. One person stopped at chapter 3, while the other continued to chapter 5. To catch up, the first person's bookmark simply moves from chapter 3 to chapter 5.

Non-fast-forward

Now imagine two people started from chapter 3 and wrote different endings. Their work has diverged. Someone has to combine the two versions and record that combination. The merge commit represents that joining point.

Common Commands

Allow Fast-Forward When Possible

git merge feature

This is the normal merge command. Git will fast-forward if possible; otherwise it performs a non-fast-forward merge.

Require a Merge Commit

git merge --no-ff feature

This prevents Git from fast-forwarding and creates a merge commit.

Allow Only Fast-Forwarding

git merge --ff-only feature

This tells Git that the merge must be a fast-forward. If the branches have diverged, Git refuses to perform the merge rather than creating a merge commit.

Why This Matters in Team Development

The choice between fast-forward and non-fast-forward merging is not just about Git mechanics. It affects how your project's history looks.

A project that favors fast-forward merges tends to maintain a cleaner, linear history. A project that favors explicit merge commits preserves more information about when separate branches were integrated.

Neither approach is universally better. Teams often choose a strategy based on how they want their Git history to communicate the development process.

In Summary

The easiest way to remember the difference is:

  • Fast-forward merge: the target branch is behind the feature branch, so Git can simply move the target pointer forward.
  • Non-fast-forward merge: the branches have diverged, so Git needs a new merge commit to join their histories.
  • --no-ff: forces Git to create a merge commit even when a fast-forward would be possible.
  • --ff-only: allows the merge only when Git can fast-forward.

In short: fast-forwarding moves a pointer; non-fast-forwarding creates a new commit to join two histories.


See All Posts on GitHub    « Previously    Next »

Why You Cannot Directly Check Out a Remote Branch in Git

See All Posts on GitHub    « Previously    Next »

Why You Cannot Directly Check Out a Remote Branch in Git

A common point of confusion in Git is the difference between a remote branch and a local branch. You may see a branch such as origin/feature-login and naturally think, "I should be able to check it out."

The important idea is this: origin/feature-login is not a normal local branch. It is a remote-tracking reference that tells your local Git repository what the corresponding branch on the remote repository looked like when Git last updated its information.

1. What Is a Remote Branch?

Suppose your team has a remote repository called origin. Someone has pushed a branch called feature-login to that repository.

After fetching the latest information, your repository might show:

origin/main
origin/feature-login
origin/develop

At first glance, origin/feature-login looks like a branch that you can simply switch to. But it is better to think of it as a remote-tracking reference.

It represents your local record of the branch feature-login on the remote named origin. It is not the same thing as having a local working branch named feature-login.

Remote repository | | branch: feature-login v origin/feature-login | | remote-tracking reference v Local repository feature-login | | local branch v Your working directory

2. What Does git fetch Actually Do?

When you run:

git fetch origin

Git contacts the remote repository and downloads information about commits and branches that you do not yet have locally.

It then updates remote-tracking references such as:

origin/main
origin/feature-login

Importantly, git fetch does not normally create or switch your local working branch.

Think of fetching as saying:

"Tell me what exists on the remote and bring the new commits into my local repository, but don't change the branch I'm currently working on."

3. Why Can't You Treat origin/feature-login Like Your Local Branch?

A local branch is something you normally work on. It has a branch name, moves forward as you create commits, and is associated with your working directory when you check it out.

A remote-tracking reference such as origin/feature-login serves a different purpose. It is Git's local representation of where the remote branch was last observed.

Local branch Remote-tracking reference
feature-login origin/feature-login
Used for local development Represents the remote branch locally
Can be checked out normally Not intended to be your normal working branch
Moves as you make commits Moves when Git updates it from the remote
Can have an upstream branch Usually identifies the upstream branch itself

4. The Solution: Create a Local Branch That Tracks It

If you want to work on the remote branch, the usual approach is to create a local branch that tracks the remote branch.

For example:

git switch -c feature-login --track origin/feature-login

This command does two things:

  1. Creates a local branch called feature-login.
  2. Configures it to track origin/feature-login.

You now have a normal local branch that you can work on.

Remote branch origin/feature-login | | -- tracks --> | Local branch feature-login | v Working directory

5. What Does "Tracking" Mean?

Tracking is one of the most useful concepts to understand when working with Git branches.

When your local feature-login branch tracks origin/feature-login, Git knows that these two branches are related.

This allows Git to understand what you mean when you run commands such as:

git pull

Instead of having to specify the remote and branch every time, Git can use the configured upstream branch.

Similarly, when you run:

git push

Git can know where the local branch is intended to push its commits.

Tracking does not mean that the two branches are permanently identical. It simply tells Git which remote branch is the upstream counterpart of your local branch.

6. An Easier Way to Create the Tracking Branch

Git provides a convenient shortcut. If the remote branch exists and there is no conflicting local branch with the same name, you can often simply run:

git switch feature-login

Git can recognize that origin/feature-login exists and automatically create a local feature-login branch that tracks it.

The older, widely used equivalent is:

git checkout -b feature-login origin/feature-login

This explicitly says:

  • Create a local branch named feature-login.
  • Start it from origin/feature-login.
  • Check out the newly created local branch.

7. What Happens When You Pull?

Suppose you have created:

feature-login → tracks → origin/feature-login

Someone else then adds commits to the remote branch.

You can update your knowledge of the remote with:

git fetch origin

Your local remote-tracking reference now moves:

origin/feature-login

Your local branch may still be where it was:

feature-login

When you subsequently run:

git pull

Git uses the tracking relationship to determine which remote branch to fetch from and integrate into your current branch.

Before fetch: feature-login -------- A -------- B origin/feature-login -- A -------- B Someone pushes C to remote: feature-login -------- A -------- B origin/feature-login -- A -------- B -------- C After git pull: feature-login -------- A -------- B -------- C origin/feature-login -- A -------- B -------- C

8. What Does "Merge With It" Mean?

The phrase "merge with the remote branch" can be slightly misleading. You do not normally merge directly with a server-side branch.

Instead, Git first represents the remote branch locally as a remote-tracking reference such as:

origin/feature-login

You can then merge that reference into your local branch:

git merge origin/feature-login

For example, imagine your local branch contains:

A --- B --- C
          \
           D --- E    (feature-login)

Meanwhile, the remote-tracking branch has advanced:

A --- B --- C --- F --- G
          \
           D --- E    (feature-login)

Running:

git merge origin/feature-login

tells Git to integrate the commits represented by origin/feature-login into your current local branch.

9. A Typical Workflow

A practical workflow might look like this:

Step 1: Fetch the remote information

git fetch origin

Step 2: See the remote branches

git branch -r

You might see:

origin/main
origin/develop
origin/feature-login

Step 3: Create a local tracking branch

git switch -c feature-login --track origin/feature-login

Step 4: Work normally

git add .
git commit -m "Add login validation"

Step 5: Push your local commits

git push

Because the local branch is tracking origin/feature-login, Git knows where the commits should be pushed.

10. One Important Correction to the Original Statement

The statement:

"You cannot check out a remote branch."

is useful as a beginner-friendly rule, but it is technically a little too absolute.

Git can check out a remote-tracking reference directly. For example, commands such as:

git checkout origin/feature-login

can put you into a state where HEAD is detached.

You can inspect the code, but you are not working on a normal local branch. If you make commits there, Git does not have a local branch name that automatically moves forward with those commits.

This is why the better practical advice is:

Don't use a remote-tracking reference as your normal working branch. Create a local branch that tracks it instead.

11. The Mental Model to Remember

The easiest way to remember the distinction is to think of the three names as different things:

Name Meaning
feature-login Your local working branch.
origin/feature-login Your local record of the remote branch.
origin The name of the remote repository.

So when someone says:

feature-login tracks origin/feature-login

it means:

Your computer Local branch feature-login | | tracks v Remote-tracking reference origin/feature-login | | represents v Branch on remote repository feature-login

12. The Key Takeaway

A remote branch and a local branch are not the same thing. After git fetch, Git gives you a remote-tracking reference such as origin/feature-login. This lets you see and work with information about the remote branch without making that reference your local working branch.

If you want to develop on that branch, create a local branch from it and configure the local branch to track the remote branch:

git switch -c feature-login --track origin/feature-login

Once the tracking relationship exists, commands such as git pull and git push become much more convenient because Git knows the relationship between your local branch and its remote counterpart.

In one sentence:

origin/feature-login is Git's local reference to a remote branch; to work on it normally, create a local feature-login branch that tracks origin/feature-login.


See All Posts on GitHub    « Previously    Next »

Difference between "git pull" and "git fetch"

See All Posts on GitHub    « Previously    Next »

Difference Between git fetch and git pull

When working with Git, you often need to get the latest changes from a remote repository. Two commands commonly used for this are git fetch and git pull. Although they are related, they behave quite differently.

What Does git fetch Do?

git fetch downloads the latest commits and other changes from the remote repository, but it does not change your current working branch.

git fetch origin

Think of git fetch as saying: “Show me what has changed on the remote, but don't apply those changes yet.”

This makes git fetch a safer option when you want to inspect incoming changes before integrating them into your work.

What Does git pull Do?

git pull downloads changes from the remote repository and then immediately integrates them into your current branch.

git pull origin main

In simple terms, git pull is roughly equivalent to:

git fetch
git merge

Depending on your Git configuration and the situation, the integration step may use a merge or rebase.

Key Difference

Feature git fetch git pull
Downloads remote changes Yes Yes
Changes your current branch No Yes
Lets you inspect changes first Yes Not by default
Can cause merge conflicts No Yes
Easy way to remember:
git fetch = “Download the changes.”
git pull = “Download and integrate the changes.”

Which One Should You Use?

Use git fetch when you want more control and want to review remote changes before incorporating them. Use git pull when you are comfortable bringing the latest remote changes directly into your current branch.

For beginners, a good habit is to use git fetch when you are unsure about incoming changes, and then decide whether to merge or rebase them after reviewing what has changed.


See All Posts on GitHub    « Previously    Next »

Tuesday, August 18, 2026

Git "ls-tree" Command

~/Downloads/gh/private/some_repo (main)
$ git ls-tree

usage: git ls-tree []  [...]

    -d                    only show trees
    -r                    recurse into subtrees
    -t                    show trees when recursing
    -z                    terminate entries with NUL byte
    -l, --long            include object size
    --name-only           list only filenames
    --name-status         list only filenames
    --object-only         list only objects
    --[no-]full-name      use full path names
    --[no-]full-tree      list entire tree; not just current directory (implies --full-name)
    --format      format to use for the output
    --[no-]abbrev[=]   use  digits to display object names


~/Downloads/gh/private/some_repo (main) $ git ls-tree HEAD

100644 blob d0f41b05f672c503eec2111b14f618182389c1ac    .gitignore
040000 tree 2fce9ada01eba23ca60c25703f97e9a0d911333b    HTML
100644 blob 5e3c3c362ce8e9c520d5036dcc2036c182585831    README.md
040000 tree c551779d5363b05aa5a34f211eb68caa2262a986    analytics
040000 tree 52829da05b92a9bf66d890acb90189eba4955445    arize_phoenix_setup
040000 tree bbf89afd34a9ff7f77653881d2ad2d9838931302    blogger_creds
040000 tree f7e789b78a2d2afe9173e22fc8e46b8f1ec14c84    bugs
100644 blob 43defc0854921f22579a4d051cf2ad869bb579c7    chatgpt-kimi-2025Aug.png
040000 tree 891c6c6262971905a698e8885d39f43bd22c09b0    experiment 1
040000 tree a9b3cf308427518614d348cf62b880eb7a22c3f2    experiment 2
100644 blob 63611291cb75fdd49301c8321e9229c93b5a3e73    gemini.png
100644 blob b5e83a0b20588c5bfe22c204cf28b06995d9d427    news_cover_chatgpt_kimi.txt
100644 blob b8c5768d11347230615f35cd83c08fd1cf33c429    news_cover_gemini.txt
040000 tree 3a6d5d80c0ffd5ea943d249aea6e8a5d8f10fa8a    prompts
040000 tree c742432f1a48c523879d508e551db30ce42bef00    sqlite_db

~/Downloads/gh/private/some_repo (main) $ git ls-tree HEAD^


100644 blob d0f41b05f672c503eec2111b14f618182389c1ac    .gitignore
040000 tree 411d3cd1d8082ebbd9b1b7960ce931172650fd2b    HTML
100644 blob 5e3c3c362ce8e9c520d5036dcc2036c182585831    README.md
040000 tree c551779d5363b05aa5a34f211eb68caa2262a986    analytics
040000 tree 52829da05b92a9bf66d890acb90189eba4955445    arize_phoenix_setup
040000 tree c30b567ab1de2929bf4d6e906693a95d01ac269f    blogger_creds
040000 tree f7e789b78a2d2afe9173e22fc8e46b8f1ec14c84    bugs
100644 blob 43defc0854921f22579a4d051cf2ad869bb579c7    chatgpt-kimi-2025Aug.png
040000 tree f766fd0de8015e29dacce7e221547bc400b0d192    experiment 1
040000 tree a9b3cf308427518614d348cf62b880eb7a22c3f2    experiment 2
100644 blob 63611291cb75fdd49301c8321e9229c93b5a3e73    gemini.png
100644 blob b5e83a0b20588c5bfe22c204cf28b06995d9d427    news_cover_chatgpt_kimi.txt
100644 blob b8c5768d11347230615f35cd83c08fd1cf33c429    news_cover_gemini.txt
040000 tree 3a6d5d80c0ffd5ea943d249aea6e8a5d8f10fa8a    prompts
040000 tree 9e74c0b357a557e5602cbd115158d51493f672df    sqlite_db

~/Downloads/gh/private/some_repo (main)
$ git ls-tree HEAD HTML

040000 tree 2fce9ada01eba23ca60c25703f97e9a0d911333b    HTML

~/Downloads/gh/private/some_repo (main)
$ git ls-tree HEAD HTML/


100644 blob bd269ece__<40 Chars Long SHA Code>    HTML/19_Dropped_Out...
100644 blob 56aea3dd__<40 Chars Long SHA Code>    HTML/1_BHK_Full_Rig...

...
...
...

100644 blob fd179359__<40 Chars Long SHA Code>    HTML/Access_Denied_...
100644 blob 07eebeaf__<40 Chars Long SHA Code>    HTML/Afghanistans_D...

Wednesday, May 6, 2026

Managing Git PAT and Credentials on Ubuntu Laptop

See All Posts on GitHub    « Previously    Next »

my git push command always prompts for username and password. 
Can't I put it in environment variables or something to not having to enter it again and again? 
I am using Ubuntu and repo is on GitHub

Yes—you shouldn’t need to type your username/password every time. In fact, GitHub no longer supports password auth for Git pushes, so what you’re seeing is usually because you're using HTTPS without proper credential storage.

Here are the best ways to fix it (ranked) 👇


✅ Option 1 (Recommended): Use SSH (No prompts ever)

This is the cleanest, most developer-friendly setup.

Step 1: Generate SSH key

Bash
ssh-keygen -t ed25519 -C "your_email@example.com"

Press Enter through prompts.


Step 2: Add SSH key to ssh-agent

Bash
eval "$(ssh-agent -s)"
ssh-add ~/.ssh/id_ed25519

Step 3: Copy public key

Bash
cat ~/.ssh/id_ed25519.pub

Step 4: Add it to GitHub

Go to:
👉 GitHub → Settings → SSH and GPG Keys → New SSH Key

Paste the key.


Step 5: Switch your repo to SSH

Check current remote:

Bash
git remote -v

Change it:

Bash
git remote set-url origin git@github.com:USERNAME/REPO.git

Done 🎉

Now:

Bash
git push

👉 No username/password ever again.


✅ Option 2: Use Git Credential Helper (HTTPS but cached)

If you want to stay on HTTPS:

Store credentials permanently

Bash
git config --global credential.helper store

Then do one push:

Bash
git push

Enter username + Personal Access Token (NOT password) once.

After that, Git saves it in:

~/.git-credentials

More secure option (recommended over store)

Bash
git config --global credential.helper cache
  • Keeps credentials in memory (default 15 min)

  • You can extend:

Bash
git config --global credential.helper 'cache --timeout=3600'

⚠️ Important (GitHub authentication change)

Since 2021, GitHub requires:

  • ❌ Password → NOT allowed

  • ✅ Personal Access Token (PAT)

Create one here:
👉 GitHub → Settings → Developer Settings → Personal Access Tokens


🚫 About Environment Variables

You can technically do this:

Bash
export GIT_USERNAME=...
export GIT_PASSWORD=...

But:

  • ❌ Git doesn’t use these directly for auth

  • ❌ Not secure (visible in process/env)

  • ❌ Not standard practice

👉 So avoid this approach.


💡 My Recommendation for You

Since you're a developer (and doing frequent pushes):

👉 Switch to SSH — one-time setup, zero friction afterward.


If you want, I can:

  • Check your current repo setup (git remote -v)

  • Or give you a 1-command script to fully switch to SSH 🚀




I prefer this Option 2: Use Git Credential Helper (HTTPS but cached) 
What happens when my PAT changes after 90 days?


Good question—this is exactly where people get tripped up with HTTPS + PAT 👍


🔄 What happens when your PAT expires?

When your Personal Access Token (PAT) expires (e.g., after 90 days on GitHub):

👉 Next git push:

  • Git tries using the old cached token

  • GitHub rejects it ❌

  • You’ll see something like:

    remote: Invalid username or password
    fatal: Authentication failed

🧠 What Git does internally

If you're using:

1. credential.helper store

  • Old PAT is saved in:

    ~/.git-credentials
  • Git keeps using it → keeps failing until you replace it


2. credential.helper cache

  • Stored in memory only

  • Once expired → you’ll be prompted again automatically

👉 This is why cache is smoother for PAT rotation.


🛠️ How to fix when PAT expires

Option A: Just retry push (simplest)

Bash
git push
  • It will prompt again

  • Enter:

    • Username: your GitHub username

    • Password: new PAT

👉 Git updates stored credentials automatically (for both store and cache)


Option B: Manually remove old credentials (if stuck)

If Git keeps failing without prompting:

For store:

Bash
nano ~/.git-credentials

Delete the line with GitHub credentials.

Or:

Bash
rm ~/.git-credentials

Then:

Bash
git push

Option C: Force Git to forget credentials

Bash
git credential reject

Or:

Bash
git config --global --unset credential.helper
git config --global credential.helper store

🔐 Best Practice for You

Since you're on Ubuntu and want low friction:

👉 Recommended setup:

Bash
git config --global credential.helper cache
git config --global credential.helper 'cache --timeout=86400'
  • 24-hour memory cache

  • No file storage

  • Smooth re-auth when PAT changes


⚡ Pro Tip (Optional but powerful)

Instead of expiring tokens every 90 days:

👉 When creating PAT on GitHub:

  • Choose longer expiry (or no expiry if allowed in your org)

  • Scope: repo (usually enough)


🧭 Summary

ScenarioWhat happensWhat you do
PAT expiresPush failsRe-enter new PAT
Using storeKeeps failing silentlyDelete ~/.git-credentials
Using cachePrompts again automaticallyJust re-enter

If you want, I can show you a one-time setup using Git Credential Manager (GCM) which is even cleaner (and handles PAT refresh better).


See All Posts on GitHub    « Previously    Next »