इस पेज पर
git commit
TL;DR: git commit आपके Staging Area (index) का snapshot लेता है और उसे Git के object डेटाबेस में स्थायी रूप से लिखता है। पर्दे के पीछे Git आपके डायरेक्टरी structure को tree objects में पैकेज करता है, metadata (author, committer, parent commit hashes, message) वाला commit object लिखता है और active branch ref को नए commit के hash की ओर point करने के लिए अपडेट करता है।
git commit वास्तव में क्या करता है?
git commit को समझने के लिए आपको यह आम गलतफ़हमी छोड़नी होगी कि Git बदलावों को “diffs” या “deltas” के रूप में track करता है। Git में हर commit पूरे project का complete snapshot होता है।
जब आप commit करते हैं, तो आप line edits की कोई सूची save नहीं कर रहे होते। इसके बजाय आप staging area की एक तस्वीर लेते हैं और उसे एक unique, permanent identifier देते हैं। अगर किसी commit में कोई फ़ाइल बदली नहीं है, तो Git उसकी duplicate copy नहीं बनाता; वह पिछले commit के मौजूदा blob object की ओर point कर देता है। इसे structural sharing कहते हैं।
graph TD
subgraph "A Git Commit Object"
C[Commit Object<br>Hash: 8a4c10...] -->|Points to| T[Root Tree Object<br>Hash: 4a2b99...]
C -->|Points to| P[Parent Commit Hash<br>Hash: 55a420...]
C -.->|Metadata| M[Author, Committer, Timestamp, Message]
end
Commit object की संरचना
Commit को अंदर से structured metadata वाली एक सरल plaintext फ़ाइल के रूप में represent किया जाता है। जब Git इस plaintext को SHA-1 का उपयोग करके hash करता है, तो 40-character का hex identifier बनता है।
एक raw commit object में ये fields होती हैं:
- tree: top-level (root) tree object का pointer (hash), जो repository की root डायरेक्टरी को represent करता है।
- parent: पिछले commit (या commits) का hash।
- Initial commit के zero parents होते हैं।
- Regular commit का एक parent होता है।
- Merge commit के दो या उससे अधिक parents होते हैं।
- author: उस व्यक्ति का नाम, email और Unix timestamp जिसने code लिखा।
- committer: उस व्यक्ति का नाम, email और Unix timestamp जिसने code commit किया (जैसे तब, जब किसी maintainer ने patch apply किया हो)।
- gpgsig: (Optional) commit sign किया गया हो तो cryptographic signature।
- message: बदलाव क्यों किए गए, यह समझाने वाला text description।
.git/ के अंदर क्या होता है (hands-on exploration)
आइए repository के अंदर झाँककर step-by-step trace करें कि Git ये tree और commit objects कैसे बनाता है।
चरण 1: File stage करें और index जाँचें
पहले सुनिश्चित करें कि repository initialized है और git add का उपयोग करके एक फ़ाइल staged है।
$ mkdir git-commit-demo && cd git-commit-demo
$ git init
Initialized empty Git repository in /git-commit-demo/.git/
$ echo "console.log('hello world');" > app.js
$ git add app.js
इस बिंदु पर फ़ाइल एक blob object में compress होकर .git/index फ़ाइल में record हो जाती है। आइए verify करें:
$ git ls-files --stage
100644 6be02374db118b9fb99cd98c6b403e5a558d0d57 0 app.js
40-character hash 6be02374db118b9fb99cd98c6b403e5a558d0d57 उस blob के लिए Git का SHA-1 ID है जिसमें console.log('hello world'); है (echo द्वारा लिखी गई newline सहित 27 bytes, यानी कुल 28 bytes)। अगर आपका editor या shell line को अलग तरह से लिखता है, तो आपका blob hash अलग होगा — content addressing में hash फ़ाइल के exact bytes से compute होता है।
इस lesson में दिए hashes पर एक नोट: ऊपर दिया गया
app.jsblob hash (6be02374…) exact और reproducible है, क्योंकि blob केवल फ़ाइल content पर निर्भर करता है। Commit और tree hashes में आपके author/committer timestamps और timezone शामिल होते हैं, इसलिए नीचे outputs में दिखाए गए abbreviated commit hashes (जैसेd4a123f) representative हैं। इन exact कमांड्स को fresh repository में चलाने पर वही structure बनेगा, लेकिन commit और tree IDs अलग होंगे।
चरण 2: Commit बनाएँ
अब अपना पहला commit लिखते हैं।
$ git commit -m "feat: introduce logging script"
[main (root-commit) d4a123f] feat: introduce logging script
1 file changed, 1 insertion(+)
create mode 100644 app.js
आइए .git/refs/heads/ के अंदर branch references जाँचें। Active branch फ़ाइल main में अब commit का पूरा 40-character hash होना चाहिए:
$ cat .git/refs/heads/main
d4a123f4c7b8d80c0ef14815a5bb1d6a362bf2c9
और .git/HEAD Git को बताता है कि कौन-सी branch active है:
$ cat .git/HEAD
ref: refs/heads/main
चरण 3: Commit object को deconstruct करना
आइए plumbing कमांड git cat-file का उपयोग करके बनाए गए commit object का निरीक्षण करें:
# Print type of the object
$ git cat-file -t d4a123f4c7b8d80c0ef14815a5bb1d6a362bf2c9
commit
# Print raw content of the commit object
$ git cat-file -p d4a123f4c7b8d80c0ef14815a5bb1d6a362bf2c9
tree b8a27d0ef093ab7e20cf7b68a417534431e21b7b
author Purna Pattela <purna@example.com> 1772960000 +0000
committer Purna Pattela <purna@example.com> 1772960000 +0000
feat: introduce logging script
ध्यान दें कि commit object में कोई फ़ाइल content और कोई diff नहीं है। वह b8a27d0... hash वाले tree object की ओर point करता है। आइए उस tree का निरीक्षण करें:
$ git cat-file -p b8a27d0ef093ab7e20cf7b68a417534431e21b7b
100644 blob 6be02374db118b9fb99cd98c6b403e5a558d0d57 app.js
Tree object डायरेक्टरी listing की तरह काम करता है। यह filename app.js को फ़ाइल permissions (100644) और zlib-compressed content blob (6be0237...) से map करता है।
चरण 4: Subdirectories जोड़ना और parents verify करना
आइए एक subdirectory जोड़कर देखें कि Git nested फ़ोल्डर को कैसे handle करता है।
$ mkdir src
$ echo "const port = 3000;" > src/config.js
$ git add src/config.js
$ git commit -m "chore: add source config"
[main 7f2b58c] chore: add source config
1 file changed, 1 insertion(+)
create mode 100644 src/config.js
आइए इस दूसरे commit को पढ़ें:
$ git cat-file -p 7f2b58c
tree a832dbf521b4a56c071d2b512c98a3e74211a512
parent d4a123f4c7b8d80c0ef14815a5bb1d6a362bf2c9
author Purna Pattela <purna@example.com> 1772960100 +0000
committer Purna Pattela <purna@example.com> 1772960100 +0000
chore: add source config
नई line parent d4a123f... पर ध्यान दें। यह पहले commit की ओर वापस point करती है और उन्हें history chain में जोड़ती है।
आइए इस नए commit के tree को देखें:
$ git cat-file -p a832dbf521b4a56c071d2b512c98a3e74211a512
100644 blob 6be02374db118b9fb99cd98c6b403e5a558d0d57 app.js
040000 tree f3b3a3c9e6db5832a875a6c11d2b270a2936274b src
Flat फ़ाइल list दिखाने के बजाय root tree object एक दूसरे tree object की ओर point करता है (जो src/ डायरेक्टरी को represent करता है और mode 040000 से indicated है)। Nested src tree के अंदर झाँकें:
$ git cat-file -p f3b3a3c9e6db5832a875a6c11d2b270a2936274b
100644 blob c8384df24a5611dfd9a1db4b86270a1a8a25c156 config.js
यह tree-within-a-tree architecture आपके filesystem को बिल्कुल mirror करता है।
विस्तृत कमांड विकल्प और उन्नत उपयोग
1. Last commit में amendment (git commit --amend)
अगर आपके commit message में typo हो जाए या आप commit करने से पहले कोई change stage करना भूल जाएँ, तो last commit में amendment कर सकते हैं:
$ git add forgotten_file.txt
$ git commit --amend -m "chore: add source config and settings"
अंदर की बात: चूँकि Git में commits immutable होते हैं (उनका SHA-1 hash उनके contents से compute होता है), Git मौजूदा commit को edit नहीं कर सकता। इसके बजाय --amend एक बिल्कुल नया commit object बनाता है, जो नए tree और modified message की ओर point करता है और जिसका parent पुराने commit के parent पर set होता है। फिर Git branch pointer को नए commit पर update करता है और पुराने commit को orphaned छोड़ देता है।
graph TD
subgraph "How Amend Works"
C1[Commit 1] --> C2[Commit 2: Original]
C1 --> C3[Commit 3: Amended]
Branch[Branch Ref: main] -->|Moved from C2 to C3| C3
end
style C2 fill:#faa,stroke:#333
style C3 fill:#afa,stroke:#333
2. Staging Area छोड़ना (git commit -a)
आप -a (या --all) flag का उपयोग करके staging छोड़ सकते हैं। इससे Git सभी modified और deleted फ़ाइलें को अपने-आप stage करता है और तुरंत commit लिख देता है:
$ git commit -am "refactor: simplify logger interface"
[!WARNING] यह कमांड नई untracked फ़ाइलें को stage नहीं करेगा। अगर आप बिल्कुल नई फ़ाइल बनाते हैं, तो
git commit -aचलाने से पहलेgit addचलाना ज़रूरी है।
3. GPG या SSH से commits sign करना (git commit -S)
Production environments में यह verify करना बहुत ज़रूरी है कि commits वास्तव में सूचीबद्ध author से ही आए हैं। आप अपने commits को cryptographically sign कर सकते हैं:
$ git commit -S -m "feat: release critical payment gateway integration"
यह commit को local private key का उपयोग करके sign करता है। GitHub या GitLab इसके बाद आपकी public key के साथ signature match करके commit के आगे “Verified” badge दिखा सकते हैं।
वास्तविक स्थिति: Atomic commits लिखना
एक healthy codebase atomic commits पर निर्भर करता है। Atomic commit वह commit है जो एक, और केवल एक, logical काम करता है। इसमें किसी एक फ़ीचर को implement करने या किसी एक bug को fix करने के लिए ज़रूरी सभी changes होने चाहिए, और कुछ नहीं।
उदाहरण के लिए, अगर आप authentication system बना रहे हैं, तो implemented login and fixed layout issues on homepage जैसा commit message न लिखें। अगर homepage layout fix से कुछ और टूटता है, तो उस commit को revert करने पर login system भी delete हो जाएगा!
इसके बजाय git add का उपयोग करके काम को अलग-अलग commits में बाँटें:
# First, stage only the layout changes
$ git add src/styles.css
$ git commit -m "fix(styles): repair home page navigation alignment"
# Next, stage the login files
$ git add src/auth/
$ git commit -m "feat(auth): implement user authentication mechanism"
यह साफ़ structure git log का उपयोग करके history audit करना आसान बनाता है और developers को problems को सुरक्षित रूप से isolate करने देता है।
आम pitfalls और गलतियाँ
1. Pushed commits में amendment करना
किसी ऐसे commit पर कभी git commit --amend न चलाएँ जो पहले ही दूसरे developers के साथ share की गई remote repository (जैसे GitHub) पर push हो चुका हो।
- क्यों: Amendment से commit hash बदल जाता है। अगर आप इस amended commit को force-push करते हैं, तो यह सभी के लिए history overwrite कर देगा और उनके pull करने पर मुश्किल merge conflicts पैदा होंगे।
- सामान्य नियम: केवल उन commits में amendment करें जो सिर्फ आपके local computer पर मौजूद हैं।
2. खाली कमिट की त्रुटि
अगर staged changes न होने पर git commit चलाने की कोशिश करते हैं, तो Git कमांड reject कर देगा:
$ git commit -m "nothing changes"
On branch main
nothing to commit, working tree clean
- Fix: अगर आपको जानबूझकर बिना फ़ाइल modifications के commit बनाना है (उदाहरण के लिए CI/CD build runner trigger करने के लिए), तो override flag इस्तेमाल करें:
$ git commit --allow-empty -m "trigger: run test suite in pipeline"
3. Author और committer को लेकर भ्रम
कभी-कभी project histories में आपको एक commit में Author और Committer के रूप में अलग-अलग लोग दिखाई देंगे।
- Author: वह व्यक्ति जिसने actual code changes लिखे।
- Committer: वह व्यक्ति जिसने commit apply किया (जैसे
git rebaseका उपयोग करके या patch apply करके)। - आम तौर पर दोनों एक ही व्यक्ति होते हैं। लेकिन अगर आप GitHub पर किसी और की pull request को rebase या squash करते हैं, तो आप Committer बन जाते हैं, जबकि वे Author बने रहते हैं।
कमांड त्वरित संदर्भ
| Command | Action |
|---|---|
git commit | Multi-line commit message लिखने के लिए आपका terminal editor खोलता है। |
git commit -m "msg" | Short inline message के साथ staged changes commit करता है। |
git commit -a -m "msg" | Modified/deleted फ़ाइलें को stage करके एक ही step में commit करता है। |
git commit --amend | Last commit में amendment करता है (नई staged फ़ाइलें जोड़ता है और/या message अपडेट करता है)। |
git commit --amend --no-edit | नई फ़ाइलें के साथ last commit में amendment करता है, लेकिन exact same message रखता है। |
git commit --allow-empty -m "msg" | कोई changes staged या modified न होने पर भी commit force करता है। |
git commit -S -m "msg" | Commit को cryptographically sign करता है। |
FAQ (लोग अक्सर पूछते हैं)
git commit और git push में क्या अंतर है?
git commit एक local कमांड है। यह आपके local डायरेक्टरी डेटाबेस (.git/objects/) में snapshot record करता है। यह किसी external server से संपर्क नहीं करता। git push एक network कमांड है, जो इन local commits, trees और blobs को remote repository (जैसे GitHub, GitLab या self-hosted server) पर upload करती है, ताकि उन्हें दूसरों के साथ share किया जा सके।
Git 40-अक्षर वाला commit hash कैसे compute करता है?
Git commit object के header और content को SHA-1 से hash करता है। Content में tree hash, parent hash(es), author info, committer info और commit message शामिल होते हैं। चूँकि hash calculation में current timestamp भी शामिल होता है, इसलिए बिल्कुल समान फ़ाइलें और message के साथ बनाए गए दो commits के hashes फिर भी अलग होंगे।
क्या मैं अपना last commit revert या delete कर सकता हूँ?
हाँ। अगर आपने changes commit कर दिए हैं, लेकिन changes को working डायरेक्टरी में रखते हुए (उन्हें unstage करके) commit undo करना चाहते हैं, तो चलाएँ:
git reset --soft HEAD~1
अगर आप last commit को पूरी तरह erase करना और सभी modifications discard करना चाहते हैं, तो चलाएँ:
git reset --hard HEAD~1
(Warning: git reset --hard को आसानी से undo नहीं किया जा सकता; इसे चलाने से पहले अपना काम verify कर लें!)
Detached HEAD स्थिति क्या है?
अगर आप branch के बजाय किसी specific commit hash (जैसे git checkout a1b2c3d - link git-checkout) को checkout करते हैं, तो आपका HEAD pointer branch reference के बजाय सीधे commit object की ओर point करता है। अगर आप इस state में commit करते हैं, तो कोई branch नए commit की ओर point नहीं करेगी। अगर आप branches switch करते हैं, तो आपका नया commit orphaned हो जाएगा और अंततः Git की garbage collection से delete हो जाएगा। Commit करने से पहले हमेशा सुनिश्चित करें कि आप किसी branch पर हैं (link git-branch)।
अपनी जानकारी जाँचें
क्विज़ लोड हो रही है…