Tracking history with git in a team scenario
Git workflow
This article walks through a practical Git workflow for a small development
team.
We will follow three developers—Alice(admin), Bob(dev), and Carol(dev).
They work on new features, handle an urgent security fix, and integrate their
work into the main branch using commands like rebase, cherry-pick.
Setup the remote repo
To simulate a remote server for this local test, we’ll create a bare repository.
1 | git init --bare remote-repo.git |
Alice initializes the repo
1 | cd Alice |
Bob adds a login feature
1 | cd ../Bob |
Carol adds a payment feature
1 | cd ../Carol |
Alice pushes a new commit on main
1 | cd ../Alice |
Bob integrates the login feature with rebase
Rebase main into feature branch
1
2cd ../Bob
git pull origin main --rebaseBefore rebase (
feature/login):1
2
3
4╰─$ git log --graph --oneline --format="%h %<(16)%an %s"
* 4e572e0 Bob feat(auth): add password validation
* 1b9d983 Bob feat(auth): add login function with validation
* 181e112 Alice Initial commit: project setupAfter rebase (
feature/login):1
2
3
4
5╰─$ git log --graph --oneline --format="%h %<(16)%an %s"
* 3f58f24 Bob feat(auth): add password validation
* ab83b58 Bob feat(auth): add login function with validation
* 6fe7ef2 Alice chore: add version configuration
* 181e112 Alice Initial commit: project setup
Merge feature branch into main
1
2
3git checkout main
git merge --no-ff feature/login -m "Merge pull request #1: Add login functionality"
git push origin mainThe main branch git log graph right now:
1
2
3
4
5
6
7
8╰─$ git log --graph --oneline --format="%h %<(16)%an %s"
* 8c8fad5 Bob Merge pull request #1: Add login functionality
|\
| * 3f58f24 Bob feat(auth): add password validation
| * ab83b58 Bob feat(auth): add login function with validation
| * 6fe7ef2 Alice chore: add version configuration
|/
* 181e112 Alice Initial commit: project setupDelete unused feature branch on remote and local
1
2git push origin --delete feature/login
git branch -d feature/login
Merging instead of rebasing results in a non-linear history.
1
2
3
4
5
6
7
8
9git pull origin main --no-rebase
git log --graph --oneline --format="%h %<(16)%an %s"
* 301189c Bob Merge branch 'main' of /home/jooooody/git_test/temp/remote-repo into feature/login
|\
| * c56c42b Alice chore: add version configuration
* | df9c340 Bob feat(auth): add password validation
* | 477ca6f Bob feat(auth): add login function with validation
|/
* 1dd69a3 Alice Initial commit: project setup1
2
3
4
5
6
7
8
9
10
11
12
13
14git checkout main
git merge --no-ff feature/login -m "Merge pull request #1: Add login functionality"
git log --graph --oneline --format="%h %<(16)%an %s"
* a7bbfe4 Bob Merge pull request #1: Add login functionality
|\
| * 301189c Bob Merge branch 'main' of /home/jooooody/git_test/temp/remote-repo into feature/login
| |\
| | * c56c42b Alice chore: add version configuration
| |/
|/|
| * df9c340 Bob feat(auth): add password validation
| * 477ca6f Bob feat(auth): add login function with validation
|/
* 1dd69a3 Alice Initial commit: project setup
Alice adds more feature on main
1 | cd ../Alice |
Bob hotfix
1 | cd ../Bob |
main branch git graph:
1 | ╰─$ git log --graph --oneline --format="%h %<(16)%an %s" |
Carol continues her feature but she needs the code from hotfix
Cherry-pick hotfix code
1
2
3
4cd ../Carol
git fetch origin
git log --all --oneline | grep "fix(security): add input sanitization" # 624d97a
git cherry-pick 624d97aBefore cherry-pick (
feature/payment):1
2
3
4╰─$ git log --graph --oneline --format="%h %<(16)%an %s" 130 ↵
* 25f7a59 Carol feat(payment): add refund functionality
* 201212a Carol feat(payment): add payment processing
* 181e112 Alice Initial commit: project setupAfter cherry-pick (
feature/payment):1
2
3
4
5╰─$ git log --graph --oneline --format="%h %<(16)%an %s" 130 ↵
* b4e1093 Bob fix(security): add input sanitization
* 25f7a59 Carol feat(payment): add refund functionality
* 201212a Carol feat(payment): add payment processing
* 181e112 Alice Initial commit: project setup
Continue develop
1
2
3
4echo "// Added fraud detection" >> payment.js
git add payment.js
git commit -m "feat(payment): implement fraud detection logic"
git push origin feature/payment
Carol syncs with main using rebase
Rebase:
1
2git fetch origin
git rebase origin/mainBefore rebase (
feature/payment):1
2
3
4
5
6╰─$ git log --graph --oneline --format="%h %<(16)%an %s" 130 ↵
* ece2c62 Carol feat(payment): implement fraud detection logic
* b4e1093 Bob fix(security): add input sanitization
* 25f7a59 Carol feat(payment): add refund functionality
* 201212a Carol feat(payment): add payment processing
* 181e112 Alice Initial commit: project setupAfter rebase (
feature/payment):1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16╰─$ git log --graph --oneline --format="%h %<(16)%an %s"
* f19e516 Carol feat(payment): implement fraud detection logic
* f167f6e Carol feat(payment): add refund functionality
* 9dae6be Carol feat(payment): add payment processing
* 287625d Bob Merge hotfix: Security patch for input sanitization
|\
| * 624d97a Bob fix(security): add input sanitization
|/
* 8d3d75b Alice feat(utils): add error logging utility
* 8c8fad5 Bob Merge pull request #1: Add login functionality
|\
| * 3f58f24 Bob feat(auth): add password validation
| * ab83b58 Bob feat(auth): add login function with validation
| * 6fe7ef2 Alice chore: add version configuration
|/
* 181e112 Alice Initial commit: project setup
A force push is required because rebasing rewrites history, which changes the commit hashes.
1
git push origin feature/payment --force-with-lease
A rebase does not move your old commits. Instead, it re-applies them one by one on top of the target branch (main).
Each re-applied commit is a new commit with a different parent. Since a commit’s hash is calculated based on its content, author, timestamp, and its parent, changing the parent results in a brand new hash.
Alice merges the completed payment feature
1 | cd ../Alice |
Explore git history in carol’s workspace
git reflog
- It shows a history of where the current commit (
HEAD) pointer has been. It logs actions likecheckout,commit,reset, andmerge. - It lists these actions in reverse chronological order, with the newest action at the top
- By default, it only shows the log for
HEAD. - It’s very useful for finding lost work and recovering from mistakes in your local repository.
Carol feature/pyment branch:
1 | git reflog | cat -n |
The yellow highlights show all of the git reflog command output entry.
After rebase, commits like 201212a, 25f7a59 are gone from the branch history. They have been replaced by new commits (f19e516, f167f6e, etc.).
The old commits still exist for a while and can be found with the reflog, but they are no longer part of the branch.
What happens to the old commits in the
reflog?These “lost” commits don’t stay in your repository forever.
Git has a process called “garbage collection” (
git gc) that periodically cleans up objects that are no longer reachable from any branch or tag.The entries in
reflogalso expire (typically after 90 days). So,reflogis your safety net, but it’s not a permanent archive for abandoned history.
git reflog —all
- Show all log of references (local branch, remote branch, tag, etc.)
- Not only show current branch history, includes all branch
- For tracking all branch history
Carol git workspace (feature/payment):
1 | git reflog --all | cat -n |
- When you work with a branch that is synchronized with a remote repository, Git maintains two important and separate references in your local repository.
refs/heads/feature/payment- This is your local branch.
- Its
reflogrecords the history of changes made to this branch on your machine. This includes actions like making new commits (git commit) or resetting the branch (git reset).
refs/remotes/origin/feature/payment- This is a remote-tracking branch. It acts as a cache of the branch’s state on the remote server (origin).
- Its
reflogonly records when this branch was updated. This happens when you communicate with the remote, for example, by runninggit pushorgit fetch.
- When you delete the local branch with
git branch -D feature/payment, you are only deleting therefs/heads/feature/paymentreference.- The
reflogfile associated with your local branch is deleted along with the branch itself. - When you run
git reflog --all, you will no longer see entries forrefs/heads/feature/payment. - The remote-tracking branch
(refs/remotes/...) is unaffected and itsreflogremains, because you did not delete that reference. - You can see the old branch name directly in the HEAD
refloghistory:181e112 (HEAD -> main) HEAD@{0}: checkout: moving from feature/payment to main- Even though the branch reference is gone, some commits (f19e516, etc.) still exists in your repository for a while. Because it’s part of the HEAD
refloghistory, the HEAD isn’t gone. - Then, you use that hash to recreate the branch:
git branch feature/payment 181e112
- Even though the branch reference is gone, some commits (f19e516, etc.) still exists in your repository for a while. Because it’s part of the HEAD
- The
git rev-list Head
- It lists all reachable commits—that is, the
HEADand all its ancestors. - It works by starting at the
HEADpoints to and walking backward through the history by following each commit’s **parent(s).
Carol git workspace (feature/payment):
1 | git rev-list HEAD --oneline | cat -n |
Actions like rebase or reset change where a branch reference, it means the branch reference now points to a new chain of commits, making the old commits unreachable from this reference.
git rev-list —reflog
- The
--reflogoption tellsrev-listto use every entry from thereflogas a starting point for its search. - It lists the ancestors of every commit mentioned in the
reflog. - This command is useful for finding unreachable commits that might have been lost after operations like
rebaseorgit reset --hard.
Carol git workspace (feature/payment):
1 | git rev-list --reflog --oneline | cat -n |
Explore git history in alice’s workspace
git reflog
Carol git workspace (main branch):
1 | ╰─$ git reflog | cat -n |
git reflog —all
Carol git workspace (main branch):
1 | ╰─$ git reflog --all | cat -n |
git rev-list Head
Carol git workspace (main branch):
1 | ╰─$ git rev-list HEAD --oneline | cat -n 130 ↵ |
git rev-list —reflog
Carol git workspace (main branch):
1 | ╰─$ git rev-list --reflog --oneline | cat -n |
The outputs of rev-list HEAD and rev-list --reflog are identical. This is because main branch has a clean, forward-only history with no rebasing or resets.