Skip to content
Briebug

Developer Resource Center

Sharing our recent findings, expertise, and insights with the community.

Git Questions, Good and Bad

Git by 5 min read

Many common Git questions rest on assumptions that do not hold, because Git's internals differ from older version-control tools. This clears up the confusing ones, from commit hashes to rebasing, squashing, and safe force-pushing.

Git is the most common source control system currently. But it’s also one of the most misunderstood SCSes as well, partly because the way it works under the hood is very different from most SCSes out there. This naturally leads to a lot of questions around it, but given its different pedigree even the questions are themselves off-base or nonsensical. So, let’s run through some of the most common questions around git that are based on faulty assumptions on how git works.

Question: Why are descriptions of git so confusing?

Answer:

You’ve probably seen a graphic depiction of git branches, showing circles and arrows representing commits on those branches. One circle on the left, multiple arrows moving rightward to more circles. But the arrows are wrong. A repository’s initial commit doesn’t point to all the other commits branched off of it. Rather, all the other commits branched off of it point at the initial commit.

In other words, the arrows point backward in time, not forward. Think of how a database represents a master row with child rows: each child points to its master via a foreign key; the master knows nothing of its children. Git commits are the same.

Question: What is a commit hash? Why does git talk about hashes so much? Shouldn’t these hashes be a hidden implementation detail?

Answer:

Git treats everything—your source code and its own data structures—as ordinary text files. The contents of those text files are hashed to create a unique string. That string is used as the identifier for whatever it is.

When comparing versions of a file across branches, the filename is identical, making it confusing. But the hash of the contents is unique and stable regardless of where the file lives. If something changes, its hash changes. This is useful for diagnosing issues.

Question: Why is the dev branch behind the deploy branch even though they list the same MRs in the same order?

Answer:

Don’t diagnose by looking at the English description of the MRs. Look at the commit hash.

A commit is a text file listing authorship information. If the contents differ, the hash differs. If the hashes are different, some change exists on one branch that isn’t on the other. For that, look at the hashes of the tree file.

Question: How do I move functionality, commits, or individual files from one branch to another? How do I rebase a changeset? How do I create a new diff from an old diff?

Answer:

Some assumptions from other source control systems don’t apply to git. As a rule, don’t use language like “changesets” or “diffs.” Diffs do not exist as permanent structures since storing compressed whole files is simpler and more space-efficient.

Git represents folders via text-based tree files. Every commit points to exactly one tree file, representing the topmost folder—a snapshot in time.

The “diff” views in GitHub or GitLab are generated on the fly by comparing tree files.

Question: Two branches have diverged. How do I make them have the same commit hash again? Why does git say they’re different even when the files are identical?

Answer:

Even if all files and folders have identical content and hashes, it isn’t enough. A commit includes the hash of its parent commit. If that parent differs, then the child differs, and so on forever.

The solution is to move the branch pointer via force push.

Question: How do I fix a branch GitHub or GitLab says is behind even though it isn’t? Why is force pushing bad? Are force pushes always bad?

Answer:

A branch is just a name pointing to a commit hash. You can see this with ls .git/refs/heads or dir .git\refs\heads.

You can repoint a branch locally with git reset. A force push does the same, but remotely—changing someone else’s branch without their knowledge. Technically valid, but potentially dangerous.

Question: My rebase went weird and all my work is gone! How do I recover lost work?

Answer:

Rebasing is essentially a pointer move. Squashing is what combines commits and often causes confusion during conflict resolution.

Since git files are immutable (hashes tied to contents), your work isn’t deleted—it’s orphaned. Old commits are still on disk. Use:

  • git reflog --all
  • git log --graph --reflog

Create a new branch pointing to the commit.

Then consider rebasing onto the commit of main it’s already based on to separate squashing from rebasing.

Question: Is it safe to delete merged branches in git?

Answer:

It is safe. If you use merge commits, each commit has multiple parents. Once merged, the feature branch is just a name pointing at a commit already reachable from main. It contains no unique information.

Question: What’s the difference between rebase, squash, and merge?

Answer:

Squash: combines multiple commits into a single commit. The branch must then point to the new commit.

Rebase: changes a commit’s parent pointer. This changes hashes of the commit and all its descendants.

Merge (operation): incorporates changes from one branch into another.

Merge (strategy): instead of rewriting history (rebase), a merge commit with two parents is created. This leads to the subway-map style repo graph.

Conclusion

I hope this cleared up fundamental misconceptions about how git represents stuff under its hood, and illustrates the various ways different source control systems can operate. Git’s simplicity is part of its reliability:

  • immutability
  • content-addressed filenames
  • compression of source text
  • simple folder and commit structures
  • cheap, lightweight branches

Because the first step in answering any question is ensuring you have the right question.