सामग्री पर जाएं
·24 मिनट पढ़ने का समय

Git Beyond the Basics: वो Workflows जो हर हफ़्ते घंटे बचाते हैं

Interactive rebase, cherry-pick, bisect, worktrees, reflog rescue, और branching strategies जो वाकई काम करती हैं। Git commands जो मैं हर दिन इस्तेमाल करता हूं जो ज़्यादातर developers नहीं जानते।

साझा करें:X / TwitterLinkedIn

ज़्यादातर developers पांच Git commands सीखते हैं और रुक जाते हैं। add, commit, push, pull, merge। शायद checkout और branch अगर साहसी महसूस कर रहे हैं। यह पहले साल काम चलाता है। फिर आपकी branch में 47 commits हैं "fix" और "wip" और "please work" जैसे messages के साथ, आपने गलती से कुछ ऐसा reset कर दिया जो नहीं करना चाहिए था, और 40 minutes Stack Overflow पर बिताने पड़ते हैं गलत merge undo करने की कोशिश में।

मैं सालों से Git इस्तेमाल कर रहा हूं। सामान्य रूप से नहीं — भारी रूप से। कई branches, कई repos, कई collaborators, पूरा दिन, हर दिन। आगे जो है वो commands और workflows हैं जो मैं वास्तव में इस्तेमाल करता हूं। वो नहीं जो tutorial में अच्छे लगते हैं। वो जो मुझे हर हफ़्ते वास्तविक समय बचाते हैं।

Interactive Rebase: किसी को दिखने से पहले गड़बड़ साफ़ करो#

आपकी branch में बारह commits हैं। आधे "fix typo" हैं। एक कहता है "undo previous commit"। दूसरा कहता है "actually fix it this time"। आप PR खोलने वाले हैं। किसी को वो history देखने की ज़रूरत नहीं।

Interactive rebase इस तरह आप merge करने से पहले अपनी branch पर history rewrite करते हैं। यह आपको commits को एक साथ squash करने, messages reword करने, उन्हें reorder करने, या पूरी तरह drop करने देता है।

मूल Command#

bash
git rebase -i HEAD~5

यह editor में आपकी आखिरी 5 commits दिखाता है, सबसे पुरानी पहले:

bash
pick a1b2c3d Add user authentication endpoint
pick d4e5f6g Fix typo in auth middleware
pick h7i8j9k Add rate limiting
pick l0m1n2o Fix rate limit bug
pick p3q4r5s Update auth tests

हर line एक command से शुरू होती है। pick को इनमें से किसी में बदलें:

  • squash (या s) — इस commit को ऊपर वाली में merge करें, messages combine करें
  • fixup (या f) — Squash जैसा, लेकिन इस commit का message छोड़ दें
  • reword (या r) — Commit रखें लेकिन message बदलें
  • drop (या d) — यह commit पूरी तरह delete करें
  • edit (या e) — इस commit पर rebase रोकें ताकि आप amend कर सकें

एक वास्तविक Cleanup Session#

देखिए मैं वास्तव में क्या करता हूं। ऊपर की गंदी history बन जाती है:

bash
pick a1b2c3d Add user authentication endpoint
fixup d4e5f6g Fix typo in auth middleware
pick h7i8j9k Add rate limiting
fixup l0m1n2o Fix rate limit bug
pick p3q4r5s Update auth tests

Save करें और बंद करें। अब आपके पास पांच की बजाय तीन साफ़ commits हैं। Typo fix auth commit में fold हो गया। Rate limit bug fix rate limit commit में। आपके PR reviewer को साफ़, तार्किक progression दिखती है।

Commits को Reorder करना#

आप literally lines को rearrange कर सकते हैं। अगर test commit rate limiting commit से पहले आनी चाहिए, बस line move कर दें:

bash
pick a1b2c3d Add user authentication endpoint
pick p3q4r5s Update auth tests
pick h7i8j9k Add rate limiting

Git आपकी commits को इस नए क्रम में replay करेगा। अगर conflicts हैं, तो रुक कर आपको resolve करने देगा।

Autosquash Shortcut#

अगर आपको पता है कि commit पिछली किसी commit का fix है, commit time पर ही mark कर दें:

bash
git commit --fixup=a1b2c3d

यह fixup! Add user authentication endpoint message वाली commit बनाता है। फिर जब rebase करें:

bash
git rebase -i --autosquash HEAD~5

Git automatically fixup commits को उनके targets के नीचे reorder करता है और उन्हें fixup mark करता है। बस save करें और बंद करें। कोई manual editing नहीं।

मैं यह लगातार इस्तेमाल करता हूं। Branch पर iterate करते हुए final history साफ़ रखने का सबसे तेज़ तरीका।

Golden Rule#

कभी भी shared branch पर push हो चुके commits rebase मत करो। अगर दूसरे लोगों ने उन commits पर काम base किया है, history rewrite करने से वास्तविक समस्याएं होंगी। Merge करने से पहले अपनी feature branches rebase करें। कभी main rebase मत करें।

अगर आपने पहले से feature branch push कर दी है और rebase करना है:

bash
git push --force-with-lease

--force-with-lease flag --force से सुरक्षित है। अगर आपके आखिरी fetch के बाद किसी और ने उसी branch पर push किया है तो यह push करने से मना कर देता है। यह सभी समस्याएं नहीं रोकेगा, लेकिन सबसे आम वाली पकड़ लेता है।

Cherry-Pick: Surgical Commit Transfers#

Cherry-pick एक specific commit एक branch से लेकर दूसरी पर apply करता है। Merge नहीं। Rebase नहीं। बस एक commit, साफ़-साफ़ apply।

मैं वास्तव में कब इस्तेमाल करता हूं#

सबसे आम scenario: मैंने feature branch पर bug fix किया, लेकिन fix अभी main या release branch पर भी चाहिए। पूरी feature branch merge नहीं करनी। बस वो एक fix चाहिए।

bash
# Fix का commit hash ढूंढें
git log --oneline feature/user-auth
# a1b2c3d Fix null pointer in session validation
 
# Main पर जाएं और cherry-pick करें
git checkout main
git cherry-pick a1b2c3d

हो गया। Fix main पर एक नई commit के रूप में समान changes के साथ है।

बिना Commit किए Cherry-Pick#

कभी-कभी changes apply करना है लेकिन commit अभी नहीं करना। शायद कई cherry-picks को एक commit में combine करना है, या changes को थोड़ा modify करना है:

bash
git cherry-pick --no-commit a1b2c3d

Changes staged हैं लेकिन committed नहीं। आप उन्हें modify कर सकते हैं, और changes जोड़ सकते हैं, फिर तैयार होने पर commit करें।

Range Cherry-Pick#

कई लगातार commits चाहिए? Range syntax इस्तेमाल करें:

bash
git cherry-pick a1b2c3d..f6g7h8i

यह a1b2c3d के बाद f6g7h8i तक सब cherry-pick करता है। ध्यान दें कि a1b2c3d खुद शामिल नहीं है। अगर इसे भी शामिल करना है:

bash
git cherry-pick a1b2c3d^..f6g7h8i

Conflicts Handle करना#

Cherry-pick conflicts merge conflicts की तरह काम करते हैं। जब conflict आए:

bash
# Git बताएगा कि conflict है
# Conflicting files ठीक करें, फिर:
git add .
git cherry-pick --continue

या अगर मन बदल जाए:

bash
git cherry-pick --abort

एक बात ध्यान रखें: cherry-pick की गई commits नए commit hashes बनाती हैं। अगर बाद में original branch merge करते हैं, Git आमतौर पर duplication handle कर लेता है। लेकिन अगर branches के बीच बहुत cherry-pick करते हैं जो eventually merge होंगी, अप्रत्याशित conflicts दिख सकते हैं। Surgically इस्तेमाल करें, merge strategy के रूप में नहीं।

कुछ टूट गया। आपको पता है दो हफ़्ते पहले काम करता था। तब से 200 commits हो चुकी हैं। कौन सी ने तोड़ा?

आप हर commit manually check कर सकते हैं। या git bisect इस्तेमाल कर सकते हैं, जो binary search इस्तेमाल करता है log2(n) steps में exact breaking commit ढूंढने के लिए। 200 commits के लिए, वो 200 की बजाय लगभग 7-8 checks हैं।

Manual तरीका#

bash
# Bisecting शुरू करें
git bisect start
 
# Current commit को bad mark करें (bug यहां मौजूद)
git bisect bad
 
# एक known good commit mark करें (bug यहां नहीं था)
git bisect good v2.1.0

Git good और bad के बीच आधे रास्ते की commit checkout करता है। Test करें। फिर:

bash
# अगर इस commit पर bug मौजूद है:
git bisect bad
 
# अगर इस commit पर bug नहीं है:
git bisect good

Git हर बार range को आधा कर देता है। 7-8 steps के बाद, यह बताता है:

bash
a1b2c3d4e5f6g7h पहली बुरी commit है
commit a1b2c3d4e5f6g7h
Author: Some Developer <dev@example.com>
Date: Tue Feb 18 14:23:01 2026 +0300
 
    Refactor session handling to use async middleware

अब आपको ठीक-ठीक पता है कौन सी commit ने bug introduce किया। जब काम पूरा हो:

bash
git bisect reset

यह आपको वापस वहां ले जाता है जहां से शुरू किया था।

Automated तरीका (यही असली ताकत है)#

अगर आपके पास test है जो bug detect कर सकता है, पूरी चीज़ automate कर सकते हैं:

bash
git bisect start
git bisect bad HEAD
git bisect good v2.1.0
git bisect run npm test -- --grep "session validation"

Git automatically commits checkout करेगा, test चलाएगा, और exit code से good या bad mark करेगा। Zero मतलब good, non-zero मतलब bad। चले जाइए, वापस आइए, और exact commit बता देगा।

कोई भी script इस्तेमाल कर सकते हैं:

bash
git bisect run ./test-regression.sh

जहां test-regression.sh है:

bash
#!/bin/bash
npm run build 2>/dev/null || exit 125  # 125 मतलब "यह commit skip करो"
npm test -- --grep "session" || exit 1  # 1 मतलब "bad"
exit 0                                   # 0 मतलब "good"

Exit code 125 विशेष है — यह bisect को बताता है कि यह commit skip करे (उपयोगी जब कोई commit compile नहीं होती)। यह उन features में से एक है जो niche लगता है जब तक ज़रूरत नहीं पड़ती, और तब पूरी दोपहर बचा लेता है।

एक वास्तविक Bisect Session#

व्यवहार में ऐसा दिखता है:

bash
$ git bisect start
$ git bisect bad HEAD
$ git bisect good abc1234
Bisecting: 97 revisions left to test after this (roughly 7 steps)
[def5678...] Commit message here
 
$ npm test -- --grep "login flow"
# Tests fail
 
$ git bisect bad
Bisecting: 48 revisions left to test after this (roughly 6 steps)
[ghi9012...] Another commit message
 
$ npm test -- --grep "login flow"
# Tests pass
 
$ git bisect good
Bisecting: 24 revisions left to test after this (roughly 5 steps)
...
 
# ~7 iterations के बाद:
abc1234def5678ghi पहली बुरी commit है

200 commits के ढेर में सुई ढूंढने के लिए सात steps।

Git Worktrees: Multiple Branches, Zero Stashing#

यह सबसे कम इस्तेमाल होने वाला Git feature है। मुझे पूरा यकीन है कि ज़्यादातर developers को इसके बारे में पता ही नहीं।

समस्या: आप feature branch पर गहरे काम में हैं। हर जगह files बदली हुई हैं। तभी कोई कहता है "क्या तुम एक production bug जल्दी देख सकते हो?" आपके तीन विकल्प हैं:

  1. सब कुछ stash करो, branches switch करो, fix करो, वापस switch करो, stash pop करो। उम्मीद करो कुछ गलत न हो।
  2. अपना अधूरा काम "wip" message से commit करो। बदसूरत लेकिन काम चलाने वाला।
  3. Repo को दूसरी directory में फिर से clone करो।

या विकल्प 4: worktrees

Worktree क्या है#

Worktree एक ही repository से जुड़ी दूसरी (या तीसरी, या चौथी) working directory है। हर worktree का अपना checked-out branch है, अपनी working files हैं, अपना index है। लेकिन वे एक ही .git data share करते हैं, इसलिए पूरा repo duplicate नहीं हो रहा।

Worktree जोड़ना#

bash
# आप feature/user-auth पर हैं, गहरे काम में
# Main पर bug fix करना है:
 
git worktree add ../hotfix-session main

यह ../hotfix-session में नई directory बनाता है जिसमें main checked out है। आपकी current directory जस की तस रहती है। कुछ stash नहीं, कुछ commit नहीं, कुछ disturb नहीं।

bash
cd ../hotfix-session
# Bug fix करो
git add .
git commit -m "Fix null pointer in session validation"
git push origin main
cd ../my-project
# जैसे कुछ हुआ ही नहीं, feature पर काम जारी रखो

Worktree में नई Branch बनाना#

bash
git worktree add ../hotfix-nav -b hotfix/nav-crash main

यह worktree भी बनाता है और main के आधार पर नई branch hotfix/nav-crash भी बनाता है।

Worktrees Manage करना#

bash
# सभी worktrees की list देखें
git worktree list
# /home/dev/my-project         abc1234 [feature/user-auth]
# /home/dev/hotfix-session     def5678 [main]
 
# काम पूरा होने पर worktree हटाएं
git worktree remove ../hotfix-session
 
# अगर directory पहले ही delete हो चुकी:
git worktree prune

Stashing से यह बेहतर क्यों है#

Stashing जल्दी context switch के लिए ठीक है। लेकिन पांच minutes से ज़्यादा लगने वाली किसी भी चीज़ के लिए worktrees बेहतर हैं:

  • आपका IDE feature branch पर खुला रहता है। कोई reindexing नहीं, scroll position नहीं खोता।
  • दोनों directories में एक साथ tests चला सकते हैं।
  • Stash conflicts या यह भूलने का कोई ख़तरा नहीं कि आपने क्या stash किया।
  • एक worktree में लंबे समय चलने वाला dev server और दूसरे में साफ़ build रख सकते हैं।

मैं आमतौर पर दो-तीन worktrees active रखता हूं: मेरी मुख्य feature branch, जल्दी checks के लिए main worktree, और कभी-कभी review worktree जहां किसी और का PR checkout करता हूं।

एक पकड़#

एक ही branch दो worktrees में checkout नहीं हो सकती। यह design के अनुसार है — यह आपको एक ही branch पर दो जगह conflicting changes करने से रोकता है। कोशिश करेंगे तो Git मना कर देगा।

Reflog: सब कुछ के लिए Undo Button#

आपने hard reset किया और commits गए। Branch delete कर दी। Rebase भयानक रूप से गलत हो गया। आपको लगता है काम गया।

गया नहीं। Git लगभग कभी वास्तव में कुछ delete नहीं करता। Reflog आपकी safety net है।

Reflog क्या है#

हर बार HEAD move होता है — हर commit, checkout, rebase, reset, merge — Git इसे reflog में record करता है। यह एक log है कि आपका HEAD कहां-कहां गया है, क्रम में।

bash
git reflog

Output:

bash
a1b2c3d (HEAD -> main) HEAD@{0}: reset: moving to HEAD~3
f4e5d6c HEAD@{1}: commit: Add payment processing
b7a8c9d HEAD@{2}: commit: Update user dashboard
e0f1g2h HEAD@{3}: commit: Fix auth token refresh
i3j4k5l HEAD@{4}: checkout: moving from feature/payments to main

हर entry का एक index है (HEAD@{0}, HEAD@{1}, आदि) और क्या हुआ उसका विवरण।

Hard Reset के बाद Recovery#

आपने गलती से git reset --hard HEAD~3 चला दिया और तीन commits गईं। वे reflog में ठीक वहां हैं:

bash
# देखो क्या गया
git reflog
 
# Reset से पहले वाली commit HEAD@{1} है
git reset --hard f4e5d6c

तीनों commits वापस। संकट टला।

Delete हुई Branch की Recovery#

आपने ऐसी branch delete कर दी जिसमें unmerged काम था:

bash
git branch -D feature/experimental
# अरे नहीं, इसमें दो हफ़्ते का काम था

Commits अभी भी मौजूद हैं। उन्हें ढूंढें:

bash
git reflog | grep "feature/experimental"
# या बस reflog में उस branch की आखिरी commit देखें
 
# मिल गई। उस commit पर branch फिर से बनाएं:
git branch feature/experimental a1b2c3d

Branch वापस, अपनी सभी commits के साथ।

खराब Rebase के बाद Recovery#

Rebase किया और सब गड़बड़ हो गया। हर जगह conflicts, गलत commits, अफरातफरी:

bash
# Reflog दिखाता है rebase से पहले आप कहां थे
git reflog
# a1b2c3d HEAD@{0}: rebase (finish): ...
# ...
# f4e5d6c HEAD@{5}: rebase (start): checkout main
# b7a8c9d HEAD@{6}: commit: Your last good commit
 
# Rebase से पहले वापस जाएं
git reset --hard b7a8c9d

आप ठीक वहां वापस हैं जहां rebase शुरू होने से पहले थे। जैसे हुआ ही नहीं।

30-दिन की Safety Net#

Default रूप से, Git reflog entries 30 दिन रखता है (reachable commits के लिए 90 दिन)। उसके बाद, garbage collect हो सकती हैं। तो गलती एहसास होने के लिए आपके पास एक महीना है। व्यवहार में, यह काफ़ी से ज़्यादा है।

Expiry check कर सकते हैं:

bash
git config gc.reflogExpire
# default: 90.days.ago (reachable के लिए)
git config gc.reflogExpireUnreachable
# default: 30.days.ago (unreachable के लिए)

अगर paranoid हैं, बढ़ा दें:

bash
git config --global gc.reflogExpireUnreachable "180.days.ago"

एक व्यक्तिगत नियम#

किसी भी destructive operation से पहले — hard reset, force push, branch deletion — मैं पहले git log --oneline -10 चलाता हूं। मानसिक रूप से current HEAD note करता हूं। दो seconds लगते हैं और एक से ज़्यादा बार मुझे ऐसी panic से बचा चुका है जिसकी ज़रूरत नहीं थी।

Stash सही तरीके से: बस git stash नहीं है#

ज़्यादातर लोग stash इस तरह इस्तेमाल करते हैं:

bash
git stash
# कुछ करो
git stash pop

यह काम करता है, लेकिन यह सब कुछ "सामान" लिखे डिब्बे में फेंकने जैसा है। तीन stashes होने पर पता नहीं कौन सा क्या है।

अपने Stashes को नाम दें#

bash
git stash push -m "WIP: user auth form validation"

अब जब stashes list करें:

bash
git stash list
# stash@{0}: On feature/auth: WIP: user auth form validation
# stash@{1}: On main: Quick fix attempt for nav bug
# stash@{2}: On feature/payments: Experiment with Stripe webhooks

ठीक-ठीक दिखता है हर stash में क्या है।

Untracked Files शामिल करें#

Default रूप से, git stash सिर्फ़ tracked files stash करता है। नई files जो अभी add नहीं की वो पीछे रह जाती हैं:

bash
# सब कुछ stash करें, नई files सहित
git stash push --include-untracked -m "WIP: new auth components"
 
# Ignored files भी शामिल करें (शायद ही कभी ज़रूरत)
git stash push --all -m "Full workspace snapshot"

मैं --include-untracked लगभग हर बार इस्तेमाल करता हूं। Branch switch करते समय नई files पीछे छोड़ना भ्रम पैदा करता है।

Partial Stashing#

यह वो है जो ज़्यादातर लोग नहीं जानते। Specific files stash कर सकते हैं:

bash
# सिर्फ़ specific files stash करें
git stash push -m "Just the auth changes" src/auth/ src/middleware.ts

या patch mode इस्तेमाल करें files के अंदर specific hunks stash करने के लिए:

bash
git stash push --patch -m "Partial: only the validation logic"

Git हर change से interactively पूछेगा कि stash करना है या नहीं। y हां के लिए, n ना के लिए, s hunk को छोटे टुकड़ों में बांटने के लिए।

Apply बनाम Pop#

bash
# Pop: apply करे और stash list से हटा दे
git stash pop stash@{2}
 
# Apply: apply करे लेकिन stash list में रखे
git stash apply stash@{2}

मैं apply इस्तेमाल करता हूं जब sure नहीं कि stash साफ़ apply होगा या नहीं। अगर conflict है, stash सुरक्षित रहता है। pop के साथ, conflict होने पर stash list में रहता है (बहुत लोगों को यह पता नहीं), लेकिन मैं apply का स्पष्ट intent पसंद करता हूं।

Stash Contents देखना#

bash
# देखें stash में कौन सी files बदलीं
git stash show stash@{0}
 
# पूरा diff देखें
git stash show -p stash@{0}

Stash से Branch बनाना#

अगर stash कुछ ज़्यादा बड़ा हो गया है:

bash
git stash branch feature/auth-validation stash@{0}

यह उस commit से नई branch बनाता है जहां original stash किया था, stash apply करता है, और drop कर देता है। साफ़।

Branching Strategies: कौन सी वाकई काम करती है#

तीन मुख्यधारा branching strategies हैं। हर एक का एक संदर्भ है जहां वो चमकती है और संदर्भ जहां दर्द देती है।

Gitflow#

Classic। main, develop, feature/*, release/*, hotfix/*। Vincent Driessen ने 2010 में बनाया।

bash
# Feature branch
git checkout -b feature/user-auth develop
# ... काम ...
git checkout develop
git merge --no-ff feature/user-auth
 
# Release branch
git checkout -b release/2.1.0 develop
# ... final fixes ...
git checkout main
git merge --no-ff release/2.1.0
git tag -a v2.1.0 -m "Release 2.1.0"
git checkout develop
git merge --no-ff release/2.1.0
 
# Hotfix
git checkout -b hotfix/session-fix main
# ... fix ...
git checkout main
git merge --no-ff hotfix/session-fix
git checkout develop
git merge --no-ff hotfix/session-fix

कब काम करता है: Mobile apps, desktop software, कोई भी चीज़ जिसके named versioned releases हों और एक साथ कई versions support हों। अगर v2.1 और v3.0 ship करते हैं और दोनों patch करनी हैं, Gitflow यह handle करता है।

कब नहीं काम करता: Continuous deployment वाली web applications। अगर दिन में 5 बार production deploy करते हैं, release branches और develop branches की रस्म-अदायगी विशुद्ध overhead है। Gitflow अपनाने वाली ज़्यादातर web teams के पास develop branch perpetually टूटी रहती है और release branches कोई समझता नहीं।

GitHub Flow#

सीधा-सादा। main है। Feature branches बनाओ। PRs खोलो। main में merge करो। main deploy करो।

bash
git checkout -b feature/user-auth main
# ... काम ...
git push origin feature/user-auth
# PR खोलो, review हो, merge हो
# main हमेशा deployable

कब काम करता है: छोटी से मध्यम teams web apps ship कर रही हैं। Continuous deployment। अगर main हमेशा deploy होता है, बस इतना काफ़ी है। यही इस site पर इस्तेमाल होता है।

कब नहीं काम करता: जब कई release versions maintain करने हों, या deployment से पहले लंबा QA cycle हो। GitHub Flow मान लेता है main जल्दी production में जाता है।

Trunk-Based Development#

सब main (the "trunk") में directly commit करते हैं या बहुत short-lived branches (एक दिन से कम) से। कोई long-running feature branches नहीं।

bash
# Short-lived branch (उसी दिन merge)
git checkout -b fix/auth-token main
# ... छोटा, focused change ...
git push origin fix/auth-token
# PR review और merge घंटों में, दिनों में नहीं

कब काम करता है: High-performing teams जिनके पास अच्छी CI/CD, व्यापक test suites, और feature flags हैं। Google, Meta, और ज़्यादातर बड़ी tech companies trunk-based development इस्तेमाल करती हैं। यह छोटे, incremental changes force करता है और merge hell eliminate करता है।

कब नहीं काम करता: जिन teams के पास अच्छी test coverage या CI नहीं। अगर trunk में merge करने का मतलब untested code deploy करना है, production लगातार टूटेगा। एक दिन से ज़्यादा लगने वाली किसी भी चीज़ के लिए feature flags भी चाहिए:

bash
# Code में feature flag
if (featureFlags.isEnabled('new-checkout-flow')) {
  renderNewCheckout();
} else {
  renderLegacyCheckout();
}

मेरी सिफ़ारिश#

ज़्यादातर web development teams के लिए: GitHub Flow से शुरू करें। सीधा है, काम करता है, और GitHub/GitLab जो पहले से provide करता है उसके अलावा कोई tooling नहीं चाहिए।

अगर team 15-20 engineers से बढ़ जाए और दिन में कई बार deploy हो, feature flags के साथ trunk-based development देखें। Feature flag infrastructure में निवेश कम merge conflicts और तेज़ iteration में खुद को चुका देता है।

अगर versioned software ship करते हैं (mobile apps, CLI tools, libraries): Gitflow या इसका simplified version। Release branches वाकई चाहिए।

Strategy इसलिए मत चुनो कि किसी blog post ने कहा सबसे अच्छी है। वो चुनो जो मेल खाए आप वास्तव में कैसे ship करते हैं।

Git Hooks: वो चीज़ें Automate करो जो बार-बार भूलते हो#

Git hooks ऐसी scripts हैं जो Git workflow में specific points पर automatically चलती हैं। ये आपकी machine पर local हैं (remote पर push नहीं होतीं), यानी team के साथ share करने का तरीका चाहिए।

जो Hooks वाकई मायने रखते हैं#

pre-commit — हर commit से पहले चलता है। Linting और formatting के लिए इस्तेमाल करें:

bash
#!/bin/bash
# .git/hooks/pre-commit
 
# सिर्फ़ staged files पर ESLint चलाएं
STAGED_FILES=$(git diff --cached --name-only --diff-filter=ACM | grep -E '\.(js|ts|tsx)$')
 
if [ -n "$STAGED_FILES" ]; then
  echo "Running ESLint on staged files..."
  npx eslint $STAGED_FILES --quiet
  if [ $? -ne 0 ]; then
    echo "ESLint failed. Fix errors before committing."
    exit 1
  fi
fi
 
# Staged files पर Prettier चलाएं
if [ -n "$STAGED_FILES" ]; then
  echo "Running Prettier..."
  npx prettier --check $STAGED_FILES
  if [ $? -ne 0 ]; then
    echo "Prettier check failed. Run 'npx prettier --write' first."
    exit 1
  fi
fi

commit-msg — Commit message format validate करता है। Conventional commits enforce करने के लिए एकदम सही:

bash
#!/bin/bash
# .git/hooks/commit-msg
 
COMMIT_MSG=$(cat "$1")
PATTERN="^(feat|fix|docs|style|refactor|test|chore|perf|ci|build|revert)(\(.+\))?: .{1,72}"
 
if ! echo "$COMMIT_MSG" | grep -qE "$PATTERN"; then
  echo "Invalid commit message format."
  echo "Expected: type(scope): description"
  echo "Example: feat(auth): add session refresh endpoint"
  echo ""
  echo "Valid types: feat, fix, docs, style, refactor, test, chore, perf, ci, build, revert"
  exit 1
fi

pre-push — Push से पहले चलता है। Tests के लिए इस्तेमाल करें:

bash
#!/bin/bash
# .git/hooks/pre-push
 
echo "Running tests before push..."
npm test
if [ $? -ne 0 ]; then
  echo "Tests failed. Push aborted."
  exit 1
fi

Husky बनाम Native Hooks#

Native hooks .git/hooks/ में रहते हैं। समस्या: .git directory Git द्वारा track नहीं होती, इसलिए repo के ज़रिए hooks share नहीं कर सकते। सबको manually setup करना पड़ता है।

Husky इसे हल करता है। यह hook configurations repo में store करता है और npm install पर automatically .git/hooks setup करता है:

bash
npx husky init

यह .husky/ directory बनाता है। Files के रूप में hooks जोड़ें:

bash
# .husky/pre-commit
npx lint-staged

lint-staged के साथ मिलकर, तेज़, targeted pre-commit checks मिलते हैं:

json
{
  "lint-staged": {
    "*.{js,ts,tsx}": ["eslint --fix", "prettier --write"],
    "*.css": ["prettier --write"],
    "*.json": ["prettier --write"]
  }
}

यह ESLint और Prettier सिर्फ़ उन files पर चलाता है जो वास्तव में commit हो रही हैं। पूरे codebase पर नहीं। तेज़।

Hooks कब Skip करें#

कभी-कभी hooks चलाए बिना commit करना पड़ता है। Emergency hotfixes, अपनी branch पर work-in-progress commits:

bash
git commit --no-verify -m "WIP: debugging production issue"

संयम से इस्तेमाल करें। अगर बार-बार hooks skip कर रहे हैं, शायद hooks बहुत धीमे या बहुत सख्त हैं।

Aliases जो समय बचाते हैं#

मेरी .gitconfig सालों से विकसित हुई है। ये वो aliases हैं जो बचे — जो मैं वास्तव में हर दिन इस्तेमाल करता हूं, वो नहीं जो जोड़ी क्योंकि चतुर लगते थे।

Pretty Log#

Default git log verbose है। यह साफ़, रंगीन, graph-based view देता है:

bash
git config --global alias.lg "log --oneline --graph --all --decorate"

इस्तेमाल:

bash
git lg
# * a1b2c3d (HEAD -> main) Fix session validation
# | * d4e5f6g (feature/payments) Add Stripe integration
# | * h7i8j9k Update payment models
# |/
# * l0m1n2o Merge PR #42
# * p3q4r5s Add user dashboard

मैं दिन में 20 बार चलाता हूं। Repository की स्थिति समझने का सबसे तेज़ तरीका।

आखिरी Commit Undo करें#

Changes रखें, बस commit undo करें:

bash
git config --global alias.undo "reset HEAD~1 --mixed"

इस्तेमाल:

bash
git undo
# Commit गई, लेकिन सभी changes अभी भी working directory में हैं

जब बहुत जल्दी commit कर दिया, file भूल गया, या changes restructure करना है तो इस्तेमाल करता हूं।

सब कुछ Unstage करें#

bash
git config --global alias.unstage "reset HEAD --"

इस्तेमाल:

bash
git unstage src/auth/session.ts
# File unstage हुई लेकिन changes सुरक्षित

सभी Aliases देखें#

क्योंकि भूल जाएंगे क्या setup किया:

bash
git config --global alias.aliases "config --get-regexp ^alias\\."

और Aliases जो मैं इस्तेमाल करता हूं#

bash
# देखें क्या commit होने वाला है
git config --global alias.staged "diff --staged"
 
# Short status
git config --global alias.st "status -sb"
 
# Message बदले बिना amend करें
git config --global alias.amend "commit --amend --no-edit"
 
# आखिरी commit दिखाएं
git config --global alias.last "log -1 HEAD --stat"
 
# Merge की बजाय rebase से pull करें
git config --global alias.up "pull --rebase --autostash"
 
# Main में merge हो चुकी branches delete करें
git config --global alias.cleanup "!git branch --merged main | grep -v '^[ *]*main$' | xargs git branch -d"

up alias विशेष रूप से अच्छा है। pull --rebase history को linear रखता है merge commits बनाने की बजाय। --autostash automatically dirty files stash और restore करता है। Pull को default में ऐसा ही होना चाहिए था।

cleanup alias local branches delete करता है जो main में merge हो चुकी हैं। कुछ समय बाद दर्जनों बासी branches जमा हो जाती हैं। हफ़्ते में एक बार चलाएं।

जो Commands मैं हर दिन इस्तेमाल करता हूं#

ये aliases या advanced features नहीं हैं। बस ऐसे commands जो मैं लगातार चलाता हूं जो बहुत developers नहीं जानते लगते।

Ultimate Log Command#

bash
git log --oneline --graph --all

हर branch, हर merge, repo की पूरी topology compact view में। Pull करने के बाद सबसे पहले यही चलाता हूं। "इस repo में अभी क्या हो रहा है?" का जवाब।

Staged Changes का Diff#

bash
git diff --staged

दिखाता है क्या commit होने वाला है। Working directory में क्या बदला वो नहीं — जो वास्तव में staged है वो। Commit से पहले हमेशा चलाता हूं। हमेशा। Accidental inclusions, debug statements, console.logs जो नहीं होनी चाहिए पकड़ लेता है।

bash
# Unstaged changes देखें
git diff
 
# Staged changes देखें
git diff --staged
 
# दोनों देखें
git diff HEAD

Specific Commit दिखाएं#

bash
git show a1b2c3d

एक commit का पूरा diff दिखाता है। History review करने, समझने के लिए उपयोगी कि commit ने वास्तव में क्या बदला।

bash
# सिर्फ़ बदली files दिखाएं
git show --stat a1b2c3d
 
# Specific commit पर specific file दिखाएं
git show a1b2c3d:src/auth/session.ts

वो आखिरी वाला बहुत उपयोगी है। History में किसी भी point पर कोई भी file देख सकते हैं बिना उस commit को checkout किए।

Line Ranges के साथ Blame#

bash
git blame -L 42,60 src/auth/session.ts

दिखाता है lines 42-60 को किसने आखिरी बार modify किया। पूरी file blame करने से ज़्यादा उपयोगी, जो आमतौर पर overwhelming होता है।

bash
# Commit message भी दिखाएं, सिर्फ़ hash नहीं
git blame -L 42,60 --show-name src/auth/session.ts
 
# Whitespace changes ignore करें (बहुत उपयोगी)
git blame -w -L 42,60 src/auth/session.ts
 
# Blamed commit से पहले वाली commit दिखाएं (गहरे खोदें)
git log --follow -p -- src/auth/session.ts

-w flag महत्वपूर्ण है। इसके बिना, blame उन lines को उस व्यक्ति को attribute करेगा जिसने आखिरी बार file format की, जो शायद ही कभी वो व्यक्ति हो जिसे ढूंढ रहे हैं।

पूरी History में String ढूंढें#

bash
# पता करें कब function जोड़ा या हटाया गया
git log -S "validateSession" --oneline
 
# पता करें कब regex pattern दिखा
git log -G "session.*timeout" --oneline

-S ("pickaxe") ऐसी commits ढूंढता है जहां string की occurrences की संख्या बदली। -G ऐसी commits ढूंढता है जहां diff regex से match करता है। दोनों archeology के लिए शक्तिशाली — पता लगाना कब कुछ introduce या remove हुआ।

दो Points के बीच क्या बदला#

bash
# दो branches के बीच क्या बदला
git diff main..feature/auth
 
# इस branch पर main से diverge होने के बाद क्या बदला
git diff main...feature/auth
 
# सिर्फ़ बदली files की list
git diff main...feature/auth --name-only
 
# Stat view (files + insertions/deletions)
git diff main...feature/auth --stat

दो dots बनाम तीन dots मायने रखता है। दो dots दोनों branches के tips के बीच अंतर दिखाता है। तीन dots दिखाता है right side पर left side से diverge होने के बाद क्या बदला। Feature branch review करते समय आमतौर पर तीन dots चाहिए।

Untracked Files साफ़ करें#

bash
# देखें क्या delete होगा (dry run)
git clean -n
 
# Untracked files delete करें
git clean -f
 
# Untracked files और directories delete करें
git clean -fd
 
# Untracked और ignored files delete करें (nuclear option)
git clean -fdx

हमेशा पहले -n से चलाएं। git clean -fdx आपके node_modules, .env, build artifacts — Git द्वारा track नहीं की गई सब कुछ delete कर देगा। सच में fresh start के लिए उपयोगी, लेकिन विनाशकारी।

दूसरी Branch से Single File Restore करें#

bash
# Branch switch किए बिना main branch version की file लें
git restore --source main -- src/config/database.ts

या specific commit से:

bash
git restore --source a1b2c3d -- src/config/database.ts

यह पुराने git checkout main -- path/to/file syntax से साफ़ है, और HEAD को प्रभावित नहीं करता।

सब एक साथ: एक वास्तविक Workflow#

इन उपकरणों के साथ एक typical दिन ऐसा दिखता है:

bash
# सुबह: देखो क्या हो रहा है
git lg
git fetch --all
 
# Feature शुरू करो
git checkout -b feature/session-refresh main
 
# काम करो, incrementally commit करो
git add -p                        # पूरी files नहीं, specific hunks stage करो
git commit -m "Add token refresh endpoint"
git commit -m "Add refresh token rotation"
git commit -m "Fix: handle expired refresh tokens"
git commit -m "Add integration tests"
 
# बाधा: production bug fix करना है
git worktree add ../hotfix main
cd ../hotfix
# ... fix, commit, push, PR merged ...
cd ../my-project
git worktree remove ../hotfix
 
# Feature काम पर वापस
git commit -m "Fix edge case in token validation"
 
# PR के लिए तैयार: history साफ़ करो
git rebase -i main
# Fix commits squash करो, clarity के लिए reword करो
 
# Push करो और PR खोलो
git push -u origin feature/session-refresh
 
# Staging में कुछ टूटा? पता करो कौन सी commit:
git bisect start
git bisect bad HEAD
git bisect good main
git bisect run npm test
 
# उफ़, गलत चीज़ hard-reset कर दी
git reflog
git reset --hard HEAD@{2}

इनमें से हर command seconds लेता है। साथ मिलकर, ये घंटे बचाते हैं। काल्पनिक घंटे नहीं — वास्तविक घंटे, हर हफ़्ते, जो अन्यथा Git की गड़बड़ियां सुलझाने, manually bugs ढूंढने, या context switches में काम खोने में बिताता।

Git एक ऐसा उपकरण है जो गहराई को पुरस्कृत करता है। Basics दिन गुज़ारती हैं। लेकिन इस पोस्ट के commands वो हैं जो "मैं Git इस्तेमाल करता हूं" और "Git वाकई मुझे तेज़ बनाता है" के बीच का फ़र्क करते हैं। धीरे-धीरे सीखिए। इस हफ़्ते एक नई technique चुनिए और तब तक इस्तेमाल कीजिए जब तक muscle memory न बन जाए। फिर दूसरी चुनिए।

आपका भविष्य का self, रात 11 बजे production bug घूर रहा हो, git bisect जानने के लिए शुक्रिया अदा करेगा।

संबंधित पोस्ट