इस पेज पर

Git Branch: Git के lightweight pointer system के अंदर

संक्षेप में: Git में branch आपके source फ़ाइलें की फ़ोल्डर copy या भारी snapshot नहीं होती। Git के डिफ़ॉल्ट files reference format में यह केवल एक lightweight, बदला जा सकने वाला reference है—एक 41-byte की text फ़ाइल, जिसमें 40-character का commit SHA-1 hash और newline character होता है—जो .git/refs/heads/ डायरेक्टरी में रखी जाती है। Git में branch बनाना, rename करना या delete करना लगभग तुरंत होता है, क्योंकि इससे working डायरेक्टरी की फ़ाइलें को बदलने या copy करने के बजाय केवल ये reference फ़ाइलें बदली जाती हैं।

दायरा नोट: इस पेज पर दिया गया “41-byte फ़ाइल” वर्णन Git के डिफ़ॉल्ट files reference backend में तब सटीक है, जब branch unpacked हो। git gc references को .git/packed-refs में pack कर सकता है, और Git एक वैकल्पिक reftable backend भी support करता है, जिसमें branches अलग-अलग फ़ाइलें होती ही नहीं हैं। हर स्थिति में कमांड्स और behaviour समान रहते हैं—सिर्फ disk पर representation बदलता है। विवरण के लिए Git Refs देखें।


Branch आपकी फ़ाइलें की copy नहीं, एक ref है

Git का branching model इतना उपयोगी क्यों है, यह समझने के लिए इसे Subversion (SVN) या Perforce जैसे पुराने Version Control Systems (VCS) से तुलना करें। उन legacy systems में branch बनाने का मतलब पूरे project डायरेक्टरी tree की physical copy बनाना था। अगर आपका codebase 5 GB का है, तो branch बनाने में server पर 5 GB फ़ाइलें copy करनी पड़तीं। इसमें काफी समय और disk space लगता था, इसलिए developers छोटे tasks के लिए branches बनाने से बचते थे।

Git फ़ाइलें copy नहीं करता। इसके बजाय Git आपके project history को commit objects के Directed Acyclic Graph (DAG) के रूप में व्यवस्थित करता है। हर commit अपने parent commit(s) की ओर point करता है और history की tree जैसी structure बनती है। इस graph में:

  • Commit किसी खास समय पर project की tracked फ़ाइलें का snapshot होता है, साथ में author metadata और parent links भी होते हैं। commit object structures के विवरण के लिए /git-commit देखें।
  • Branch इस graph में किसी एक खास commit की ओर point करने वाला named reference है। यह वह starting point है जहाँ से Git parent pointers के जरिए पीछे जाकर उस branch की history बनाता है।

Branch को commits की chain के किसी node पर लगी sticky note समझें। Active branch पर काम करके नए commits बनाते समय Git इस sticky note को latest commit की ओर अपने-आप आगे बढ़ा देता है। Branch केवल reference (या “ref”) है, इसलिए Git branching operations में सिर्फ छोटी 40-character string लिखनी या बदलनी पड़ती है।

Git के सामान्य reference system को गहराई से समझने के लिए /git-refs guide पढ़ें।

तीन रेफ़रेंस में उलझना आसान है: refs/heads/main आपकी स्थानीय ब्रांच है; refs/remotes/origin/main रिमोट ब्रांच का आपका स्थानीय रिकॉर्ड है, जिसे git fetch जैसे कमांड अपडेट करते हैं; और HEAD उस ब्रांच को दिखाता है जो अभी चेक-आउट है, या detached-HEAD स्थिति में सीधे किसी कमिट को। सफल git push भी संबंधित रिमोट-ट्रैकिंग रेफ़ को अपडेट कर सकता है।


Branch बनाते समय refs/heads में बदलाव देखना

Git branches की mechanics को पूरी तरह समझने के लिए step-by-step terminal walkthrough करें और देखें कि branching operations के दौरान .git/ डायरेक्टरी कैसे बदलती है।

चरण 1: Clean Repository initialize करना

एक नया repository बनाते हैं, commit जोड़ते हैं और .git/ डायरेक्टरी की structure देखते हैं।

$ mkdir git-branch-demo
$ cd git-branch-demo
$ git init
Initialized empty Git repository in /users/admin/git-branch-demo/.git/

इस stage पर .git/refs/heads/ की contents list करने पर यह पूरी तरह empty मिलेगी:

$ ls -la .git/refs/heads/
total 0
drwxr-xr-x  2 admin  staff  64 Jul  7 10:25 .
drwxr-xr-x  4 admin  staff 128 Jul  7 10:25 ..

अभी डिफ़ॉल्ट branch फ़ाइल नहीं बनी है, क्योंकि कोई commit मौजूद नहीं है। देखते हैं कि .git/HEAD किस ओर point करता है:

$ cat .git/HEAD
ref: refs/heads/main

हालाँकि refs/heads/main अभी disk पर मौजूद नहीं है, HEAD पहले से उसी की ओर point करने के लिए configured है। इस state को “unborn branch” कहते हैं। Branch को अस्तित्व में लाने के लिए पहला commit बनाते हैं।

$ echo "Hello Git Branching!" > README.md
$ git add README.md
$ git commit -m "Initial commit"
[main (root-commit) f24e93e] Initial commit
 1 file changed, 1 insertion(+)
 create mode 100644 README.md

अब एक commit मौजूद है, इसलिए .git/refs/heads/ को फिर देखते हैं:

$ ls -la .git/refs/heads/
total 8
drwxr-xr-x  3 admin  staff   96 Jul  7 10:27 .
drwxr-xr-x  4 admin  staff  128 Jul  7 10:25 ..
-rw-r--r--  1 admin  staff   41 Jul  7 10:27 main

main नाम की फ़ाइल दिखाई दी है! इसकी contents print करते हैं:

$ cat .git/refs/heads/main
f24e93e25b90f46c6a7e0c4a45adbb474f8e6c7c

इस फ़ाइल में ठीक 40-character का SHA-1 commit hash और उसके बाद newline character है। Plumbing कमांड git cat-file से जाँचते हैं कि यह hash किस ओर point करता है:

$ git cat-file -t f24e93e25b90f46c6a7e0c4a45adbb474f8e6c7c
commit

अब commit object की contents print करके देखते हैं कि वह हमारे snapshot को कैसे represent करता है:

$ git cat-file -p f24e93e25b90f46c6a7e0c4a45adbb474f8e6c7c
tree 5b38a7c29375ab2b4a53239a584a22c5432a138b
author User <user@example.com> 1783420000 +0000
committer User <user@example.com> 1783420000 +0000

Initial commit

चरण 2: New Branch बनाना

अब git branch कमांड से feature-login नाम की नई branch बनाते हैं:

$ git branch feature-login

देखते हैं कि Git ने अंदर से क्या किया—.git/refs/heads/ की contents जाँचते हैं:

$ ls -la .git/refs/heads/
total 16
drwxr-xr-x  4 admin  staff  128 Jul  7 10:30 .
drwxr-xr-x  4 admin  staff  128 Jul  7 10:25 ..
-rw-r--r--  1 admin  staff   41 Jul  7 10:30 feature-login
-rw-r--r--  1 admin  staff   41 Jul  7 10:27 main

feature-login नाम की नई फ़ाइल बन गई है। इसकी content पढ़ते हैं:

$ cat .git/refs/heads/feature-login
f24e93e25b90f46c6a7e0c4a45adbb474f8e6c7c

इसमें main वाला बिल्कुल वही commit hash है! Git ने कोई source फ़ाइल copy नहीं की। उसने सिर्फ existing commit की ओर point करने वाली 41-byte text फ़ाइल बनाई है (डिफ़ॉल्ट unpacked files reference format मानकर)। यही बताता है कि branch creation तुरंत क्यों हो जाता है।

.git/HEAD पढ़कर अपनी active branch देखते हैं:

$ cat .git/HEAD
ref: refs/heads/main

HEAD अभी भी main की ओर point करता है, यानी हम अभी main branch पर हैं। Branch बदलने के लिए HEAD reference update करना होगा; आम तौर पर यह git checkout या git switch जैसी कमांड्स करती हैं। इस mechanism के बारे में /git-checkout में और पढ़ें।

चरण 3: Branch पर commit करना

अब नई branch पर switch करके एक बदलाव करते हैं।

$ git checkout feature-login
Switched to branch 'feature-login'

अब .git/HEAD verify करते हैं:

$ cat .git/HEAD
ref: refs/heads/feature-login

HEAD अब refs/heads/feature-login की ओर point करने के लिए update हो गया है। नया commit बनाते हैं:

$ echo "Login Form UI" > login.html
$ git add login.html
$ git commit -m "Add login page"
[feature-login a7c8e9d] Add login page
 1 file changed, 1 insertion(+)
 create mode 100644 login.html

अब अपनी branch references की contents देखते हैं:

$ cat .git/refs/heads/feature-login
a7c8e9d8928424ef245eefb925b42d3851b2c129

$ cat .git/refs/heads/main
f24e93e25b90f46c6a7e0c4a45adbb474f8e6c7c

feature-login आगे बढ़कर हमारे नए commit a7c8e9d... की ओर point कर रही है। लेकिन main अभी भी f24e93e... की ओर point करती है। /git-log से history देखें:

$ git log --oneline --graph --all
* a7c8e9d (HEAD -> feature-login) Add login page
* f24e93e (main) Initial commit

Graph दिखाता है कि feature-login diverge हो गई है। main branch pointer स्थिर है, जबकि feature-login HEAD के साथ आगे बढ़ी। बाद में इन branches को merge करने पर Git इन pointers को पढ़कर उनकी histories compute करेगा। इसके बारे में /git-merge में जानें।


Branch बनाना, rename करना और delete करना

git branch कमांड सिर्फ references बनाने के लिए नहीं है; यह branch graph को manage, query और audit करने का versatile tool है।

1. Branches की सूची और फ़िल्टरिंग

बड़े projects manage करते समय आपके पास दर्जनों local और remote branches हो सकती हैं। Git इसे व्यवस्थित रखने के लिए filters देता है।

  • सभी branches (local और remote) की list:
    $ git branch -a
    इसमें local branches एक रंग (जैसे green) और .git/refs/remotes/ में स्टोर remote-tracking branches दूसरे रंग (जैसे red) में दिखती हैं।
  • Merge status के आधार पर filter: पुरानी branches साफ करने से पहले जानना ज़रूरी है कि कौन-सी branches आपकी current branch में safely merge हो चुकी हैं:
    $ git branch --merged
    अपनी current branch में अभी merge न हुआ work देखने के लिए:
    $ git branch --no-merged
    --no-merged list की किसी branch पर git branch -d चलाना, force किए बिना, fail होगा।
  • Upstream tracking के साथ verbose list:
    $ git branch -vv
    इसमें latest commit hash, commit subject line और upstream tracking status दिखता है (जैसे local branch अपनी remote counterpart से आगे है या पीछे)।

2. डिलीशन की आंतरिक प्रक्रिया और गार्बेज कलेक्शन

Branch delete करना आम task है, लेकिन इस्तेमाल किए गए flags के अनुसार इसका व्यवहार अलग होता है।

  • Safe Delete (-d या --delete):
    $ git branch -d feature-login
    अगर branch में ऐसे commits हैं जो आपकी current branch या upstream branch से reachable नहीं हैं, तो Git उसे delete करने से रोकेगा। इससे work के accidental loss से बचाव होता है।
  • Force Delete (-D या -d --force):
    $ git branch -D feature-login
    इसका अर्थ Git से कहना है: “Reference delete करो, चाहे कुछ भी हो।”

Commits का क्या होता है?

ब्रांच हटाने से उसका रेफ़ हटता है; इससे कमिट ऑब्जेक्ट तुरंत मिटते नहीं। अगर उन कमिट तक कोई दूसरा रेफ़ नहीं पहुँचता, तो वे unreachable हो जाते हैं और कुछ समय के लिए reflog से वापस मिल सकते हैं। Git अंततः unreachable ऑब्जेक्ट prune कर सकता है; इसका समय कॉन्फ़िगरेशन और रिपॉज़िटरी की स्थिति पर निर्भर करता है। अगर काम महत्वपूर्ण है, तो सफाई से पहले दूसरी ब्रांच बना लें; ऑब्जेक्ट लाइफसाइकल के लिए Git objects देखें।

3. Upstream branches को ट्रैक करना

Collaboration के दौरान आपकी local branches remote server (जैसे GitHub) की branches को track करती हैं। यह tracking configuration .git/config में स्टोर होती है।

अपनी current active branch के लिए upstream branch set करने के लिए:

$ git branch -u origin/feature-login
# Or:
$ git branch --set-upstream-to=origin/feature-login

इससे आपकी .git/config फ़ाइल में tracking metadata जुड़ता है:

[branch "feature-login"]
    remote = origin
    merge = refs/heads/feature-login

यही configuration आपको remote और branch name बताए बिना सिर्फ git pull या git push लिखने देती है।


गलती से delete हुई branch recover करना

मान लें आप workspace साफ कर रहे हैं। आपको लगता है कि आपने feature-payment को main में merge कर दिया है, इसलिए आप चलाते हैं:

$ git branch -D feature-payment
Deleted branch feature-payment (was c3b91a2).

अचानक पता चलता है कि feature-payment merge ही नहीं हुई थी और आपने कई दिनों का work delete कर दिया! Files editor में दिखाई नहीं दे रहीं, इसलिए यह disaster जैसा लगता है।

इसे recover कैसे करें:

क्योंकि Git branches केवल references हैं, commits डेटाबेस में अभी भी मौजूद हैं। आपको सिर्फ commit hash ढूँढकर उससे नई branch reference जोड़नी है।

  1. Reflog खोजें: Reflog HEAD की position बदलने का हर अवसर record करता है। चलाएँ:
    $ git reflog
    f24e93e HEAD@{0}: checkout: moving from feature-payment to main
    c3b91a2 HEAD@{1}: commit: Add Stripe Integration and webhooks
    f24e93e HEAD@{2}: checkout: moving from main to feature-payment
    ध्यान से देखने पर पता चलता है कि c3b91a2 वह commit था जो main पर वापस switch करने से पहले feature-payment branch पर बनाया गया था।
  2. Branch pointer फिर बनाएँ: अब git branch के बाद नई branch का नाम और recovered hash देकर कमांड चलाएँ:
    $ git branch feature-payment c3b91a2
    बस! Git .git/refs/heads/feature-payment पर c3b91a2 वाली नई reference फ़ाइल बनाकर आपका work तुरंत restore कर देता है।

Branch कमांड्स कहाँ गलत हो सकती हैं

1. यह मानना कि git branch नई branch पर switch करता है

git branch new-feature चलाने से सिर्फ pointer बनता है। यह HEAD update नहीं करता। अगर आप तुरंत फ़ाइलें edit करना शुरू कर दें, तो commits पुरानी branch (जैसे main) में लिखे जाएँगे। हमेशा git checkout new-feature चलाना याद रखें (या एक ही step में create और switch करने के लिए git checkout -b new-feature इस्तेमाल करें)।

2. नेमस्पेस टकराव (File बनाम Directory)

क्योंकि Git branch refs को .git/refs/heads/ में फ़ाइलें के रूप में रखता है, इसलिए उन्हें आपके Operating System के फ़ाइल path rules का पालन करना पड़ता है।

  • अगर feature नाम की branch है, तो Git .git/refs/heads/feature नाम की फ़ाइल बनाता है।
  • इसके बाद feature/login नाम की branch बनाने पर Git को feature नाम की डायरेक्टरी बनाकर उसके अंदर login फ़ाइल रखनी होगी।
  • अधिकांश systems में एक ही parent फ़ोल्डर में समान नाम की फ़ाइल और डायरेक्टरी साथ नहीं हो सकतीं। इससे यह error आता है: error: cannot lock ref 'refs/heads/feature/login': 'refs/heads/feature' exists; cannot create 'refs/heads/feature/login'
  • Fix: अगर आप feature/login और feature/payment जैसे slash namespaces इस्तेमाल करने वाले हैं, तो feature नाम की generic branch न बनाएँ।

3. हटाई गई remote branches के पुराने local references

जब teammate remote server पर branch delete करता है, तो आपका local Git उसे .git/refs/remotes/origin/ में track करता रहता है। इससे confusion हो सकता है।

  • Fix: यह कमांड चलाकर stale remote-tracking branches साफ करें:
    $ git fetch --prune

Branch कमांड्स एक नज़र में

CommandविवरणGit Internals में कार्रवाई
git branchसभी local branches की list बनाता है.git/refs/heads/ के अंदर फ़ाइलें पढ़ता है
git branch <name>नई branch reference बनाता है.git/refs/heads/<name> पर 41-byte फ़ाइल बनाता है (डिफ़ॉल्ट unpacked files format)
git branch -d <name>merge होने पर branch को safely delete करता हैपहले reachability जाँचकर ref फ़ाइल delete करता है
git branch -D <name>branch को force delete करता हैreachability की परवाह किए बिना ref फ़ाइल delete करता है
git branch -m <new-name>current branch rename करता है.git/refs/heads/ में ref फ़ाइल rename करता है
git branch -alocal और remote branches की list बनाता है.git/refs/heads/ और .git/refs/remotes/ पढ़ता है
git branch -vvtracking status और commits दिखाता हैlocal ref फ़ाइलें को upstream settings से correlate करता है

Branch से जुड़े सवालों के जवाब

“detached HEAD” का क्या मतलब है?

“detached HEAD” state तब होती है जब HEAD किसी named branch reference के बजाय सीधे किसी specific commit hash की ओर point करता है। उदाहरण के लिए, .git/HEAD में ref: refs/heads/main के बजाय कोई कमिट ID हो सकती है। इस स्थिति में बनाई गई कमिट किसी ब्रांच पर नहीं होती; दूसरी जगह जाने के बाद वह नामित रेफ़्स से unreachable हो सकती है, हालाँकि reflog इसे वापस पाने में मदद कर सकता है। काम बचाने के लिए, उसी कमिट पर रहते हुए git switch -c rescue-work से ब्रांच बनाएँ। HEAD और ब्रांच रेफ़्स का संबंध Git refs में देखें।

क्या दो branches एक ही commit की ओर point कर सकती हैं?

हाँ। कई branches बिल्कुल एक ही commit hash की ओर point कर सकती हैं। वास्तव में main से नई branch बनाते समय दोनों branches उसी commit की ओर point करती हैं, जब तक उनमें से किसी एक पर नया commit न बने। एक ही 40-character SHA-1 string वाले कई refs से Git को कोई समस्या नहीं होती।

Local branch को remote server पर कैसे push करें?

अपनी branch को remote repository पर push करके उसे उस remote को track करने के लिए set up करने हेतु कमांड इस्तेमाल करें:

$ git push -u origin <branch-name>

-u (या --set-upstream) flag Git को tracking association बनाने के लिए कहता है। इससे .git/config फ़ाइल update होती है और इस branch पर आगे के pushes और pulls के लिए सिर्फ git push या git pull की ज़रूरत रहती है।


अपनी समझ जाँचें

क्विज़ लोड हो रही है…