Skip to content

Home / Glossary / Git

Tech & IT

Git

By Sitraka Forler · Lecturer, Durham Business SchoolUpdated 3 October 2026 About this site

A version control tool that records the changes you commit to a project as a history.

Git is installed on your own computer and saves snapshots of your files, called commits, so you can see who changed what and roll back a mistake. Branches let you build a change in isolation before it joins the shared main branch. Git is the tool; GitHub, GitLab and Bitbucket are websites that host Git repositories (project folders tracked by Git) so a team can share and review them.

The intuition

Git keeps the history of a project as a series of snapshots. Each commit records every tracked file exactly as it stood at that moment, together with an author, a time, a message and a link to the commit before it, so following those links backwards replays the life of the project one labelled picture at a time. Unchanged files are not stored twice: Git names each piece of content by a hash computed from its bytes, identical content gets the identical name, and a new snapshot simply points at what is already there. Linus Torvalds created Git in 2005 to manage development of the Linux kernel, and it shows in the design: a normal clone carries the full history and works without a network. That settles the most searched question too. Git is the program on your own machine, while hosting services such as GitHub, GitLab and Bitbucket hold a shared copy of a repository and wrap code review, issues and automation around it.

A change travels through three areas. The working tree, the working directory of many tutorials, is the folder you edit. The staging area, also called the index, is the next snapshot being assembled: git add copies a file's current content into it, so you can commit the corrected formula without the scratch file you tested it in. git commit turns the staging area into a permanent snapshot in the repository, the hidden .git folder. A branch is only a movable pointer, a name on one commit that slides forward with each new commit; creating one copies no files, and HEAD marks the branch you are on. A remote such as origin names another copy of the repository, and origin/main records where main stood there when you last fetched or pushed. Commit touches only your own repository. Push sends your new commits to the remote and moves its branch, fetch brings others' commits back without touching your files, and pull is a fetch followed by a merge or a rebase.

Trouble starts when two people move the same work forward. If main gains commits while yours sit on a branch, merge joins the two lines with a merge commit that has two parents, keeping the history as it happened. Rebase replays your commits on top of the new main for a straight line, but each replayed commit is a new commit with a new hash, so rebasing commits a colleague has already pulled leaves their copy disagreeing with yours. Git is also built for text. A notebook saved with its outputs produces unreadable diffs, a large data file weighs down every future clone, and a password committed once survives in history even after a later commit deletes it. Analysts therefore list data, outputs and .env files in .gitignore, commit small changes with messages that say why, merge through a pull request, and record the commit hash beside every number they publish, so the question of which code produced last quarter's result has an exact answer.

Example

# what has changed?
git status
# start a branch
git checkout -b feature/sharpe
# stage the change
git add returns.py
# save a snapshot locally
git commit -m "feat: add Sharpe ratio"
# upload the branch
git push -u origin feature/sharpe

One fix to an analysis script, from edit to push, with what Git prints at each step

  1. The change, and why it matters. The repository returns-analysis holds vol.py, which annualises volatility as daily_returns.std() * TRADING_DAYS ** 0.5 with TRADING_DAYS = 365. Daily returns exist only on trading days, so the factor should be the number of trading days in a year, conventionally 252. For a daily standard deviation of 0.012, √365 = 19.1050 gives 0.012 × 19.1050 = 0.229260, or 22.93%, while √252 = 15.8745 gives 0.012 × 15.8745 = 0.190494, or 19.05%: the bug overstates annual risk by 22.93 - 19.05 = 3.88 percentage points, about 20% too high, since 0.229260 / 0.190494 = 1.2035. Start a branch with git switch -c fix/trading-days (git checkout -b does the same), which prints Switched to a new branch 'fix/trading-days'. No file was copied: the new name points at the same commit as main.
  2. Edit line 3 to TRADING_DAYS = 252, save, and run git status. It prints On branch fix/trading-days, then Changes not staged for commit: with two hint lines about git add and git restore, then modified: vol.py, and ends with no changes added to commit (use "git add" and/or "git commit -a"). The edit exists in the working tree only; neither the staging area nor the repository has seen it.
  3. Read the change before you stage it. git diff opens with the header line diff --git a/vol.py b/vol.py, an index line and the two file names --- a/vol.py and +++ b/vol.py, then the hunk marker @@ -1,6 +1,6 @@, meaning a six-line window starting at line 1 in both the old and the new version. Inside it come the removed line -TRADING_DAYS = 365 and the added line +TRADING_DAYS = 252, surrounded by unchanged context lines that start with a space instead of a sign. One changed line and nothing else: no stray print, no reformatted whitespace.
  4. Stage it with git add vol.py, which prints nothing when it succeeds. git status --short now prints vol.py with an M in the first of its two status columns and a blank in the second. The left column describes the staging area and the right one the working tree, so this reads as a modification that is staged, with nothing further pending. Had you edited the file again before committing, it would read MM, an M in both columns, a warning that the commit would record the staged version and leave the newer edit behind.
  5. Commit. git commit -m "fix: annualise volatility over 252 trading days" prints [fix/trading-days 01daacc] fix: annualise volatility over 252 trading days, then 1 file changed, 1 insertion(+), 1 deletion(-). Git counts a modified line as one deletion plus one insertion, so the file's length changes by 1 - 1 = 0 lines. git status now says nothing to commit, working tree clean, and yet the commit exists on your laptop only.
  6. Look at the pointers. git log --oneline -2 prints 01daacc (HEAD -> fix/trading-days) fix: annualise volatility over 252 trading days, and below it e5c3798 (origin/main, origin/HEAD, main) feat: add annualised volatility, where origin/HEAD marks the remote's default branch. The branch name has moved forward by one commit while main and origin/main still point at e5c3798. Your own hashes will differ from these, because a commit's hash covers its author and timestamp as well as its content.
  7. Push. git push -u origin fix/trading-days prints a few progress lines, then GitHub's remote: lines with a link to open a pull request, and ends with To github.com:your-team/returns-analysis.git, then * [new branch] fix/trading-days -> fix/trading-days, then branch 'fix/trading-days' set up to track 'origin/fix/trading-days'. git status now adds Your branch is up to date with 'origin/fix/trading-days'. The commit exists in two places, and the pull request is where a colleague reviews it.
  8. Main moves while the pull request is open: a colleague merges two commits into it. git fetch updates origin/main, and git rev-list --left-right --count origin/main...HEAD prints 2 and 1, meaning two commits on main that your branch lacks and one on your branch that main lacks. git merge --no-edit origin/main prints Merge made by the 'ort' strategy. and adds a merge commit with two parents. Counting from e5c3798, the branch now holds that base, the two new commits, yours and the merge, 1 + 2 + 1 + 1 = 5 commits; 01daacc keeps its hash and a plain git push works. git rebase origin/main instead prints Successfully rebased and updated refs/heads/fix/trading-days. and leaves a straight line of 1 + 2 + 1 = 4 commits from the same base, but your commit comes back with a new hash, 319809e in this run, so the next push needs git push --force-with-lease.

Commit saves a snapshot on your machine, push copies it to the shared remote, and git status plus git log --oneline tell you at every step which of the three areas, and which branch pointers, your change has reached.

Common pitfalls

  • Treating a commit as a backup. A commit lives only in the .git folder on your laptop until you push it, and on a branch that has never been pushed git status reports working tree clean without a word about the server. Push your branch at the end of every working session, finished or not: an unmerged branch on the remote harms nobody, and a lost laptop takes every unpushed commit with it.
  • Running git add . without looking. It stages every new or changed file in the current folder and below that .gitignore does not exclude: raw data extracts, exported spreadsheets, a .env file with credentials. Run git status before every commit and list data, outputs and secrets in .gitignore. A secret deleted in a later commit is still in the history and in every existing clone, so the only real fix is to revoke it and issue a new one.
  • Rebasing or force pushing a branch someone else uses. Both rewrite commits, so colleagues who already pulled the old ones end up with histories that disagree, and a plain git push --force can overwrite commits they pushed in the meantime. Rebase only commits nobody else has, and when you must replace a branch you pushed, use git push --force-with-lease, which refuses if the branch on the remote no longer matches your last fetched copy of it.
  • Committing notebooks with their outputs, and data files next to the code. A .ipynb file is JSON that stores every output cell, so a rerun can rewrite hundreds of lines and the diff becomes unreadable, while every version of a large CSV stays in the history for good; GitHub blocks any single file larger than 100 MiB. Strip outputs before committing, for example with nbstripout, and commit the code that fetches the data rather than the data itself.
  • Reaching for the wrong undo. git restore vol.py discards your unstaged edits to that file for good, because they were never in a snapshot, and git reset --hard throws away every staged and unstaged change to your tracked files at once. To undo a commit that is already pushed, use git revert, which adds a new commit reversing it and leaves the shared history intact.

Frequently asked questions

What is Git in simple terms?

Git is a version control program that keeps the history of a project as a series of snapshots called commits. Each commit records every tracked file at one moment, who made it and why, so you can compare versions, find when a number changed and return to any earlier state. It runs on your own machine and works offline; sharing happens by pushing commits to a copy of the repository hosted elsewhere.

What is the difference between Git and GitHub?

Git is the version control program installed on your machine, and it works with no website at all. GitHub is a hosting service, like GitLab and Bitbucket, that stores a copy of a Git repository online and adds collaboration around it: pull requests for review, issues, access control and automation such as GitHub Actions. You can use Git without GitHub, but GitHub is built on Git.

What is the difference between git commit and git push?

git commit records a snapshot of the staged changes in your local repository; it needs no network and nobody else can see the result. git push sends the commits the remote does not yet have, typically to GitHub, and moves the branch there to match. A clean working tree after a commit therefore says nothing about the server: only a branch that tracks a remote adds a line such as Your branch is ahead of 'origin/main' by 1 commit, and a commit that was never pushed is lost with the laptop.

What is the staging area in Git?

The staging area, also called the index, holds the snapshot you are preparing for the next commit. git add copies a file's current content into it, and git commit records exactly what is staged, even if the file has changed again since. It lets you turn a messy afternoon into clean commits: git add -p stages individual parts of a file, so you can stage the corrected formula and leave out the print statement you added to the same file while debugging.

What is the difference between git merge and git rebase?

Both bring the new commits of another branch into yours. Merge keeps both lines of work and adds a merge commit with two parents, so the history shows what happened in parallel. Rebase replays your commits one by one on top of the other branch, which gives a straight history but creates new commits with new hashes. The usual rule is to rebase only commits nobody else has pulled, and to merge otherwise.

Why do data analysts need Git?

An analysis is code, and code changes. Git tells you which version of a script produced the numbers in a published report, lets you undo a change that broke a model, and lets a colleague review a change before it reaches the shared version. dbt projects and CI pipelines are normally run from a Git repository, so analytics engineering roles treat it as a daily tool.

Test yourself

Practise Git hands-on - free, in your browser

See it animated

Git, visually: commits, branches and merges, animated

Related terms