Khi bạn trở nên thành thạo hơn với Git, bạn sẽ gặp phải những tình huống đòi hỏi những kỹ thuật nâng cao hơn. Phần này bao gồm các tính năng Git mạnh mẽ có thể giúp bạn xử lý các tình huống và quy trình công việc phức tạp.
Kỹ thuật Rebasing nâng cao
Rebase tương tác với Fixup
Khi bạn cần thực hiện các thay đổi đối với commit trước đó, bạn có thể sử dụng tùy chọn sửa lỗi:
# Make your changes
git add .
git commit --fixup commit-hash
# Then rebase to incorporate the fixup
git rebase -i --autosquash HEAD~5
Cờ --autosquash tự động di chuyển bản sửa lỗi commit bên cạnh commit mà họ đang sửa và đánh dấu chúng là fixup.
Rebase trên một căn cứ khác
Đôi khi bạn cần di chuyển một nhánh từ cơ sở này sang cơ sở khác:
git rebase --onto new-base old-base feature-branch
Điều này sẽ khởi động lại commit giữa old-base và feature-branch lên new-base.
Ví dụ: Di chuyển một nhánh từ nhánh tính năng này sang nhánh tính năng khác:
# Before:
# main
# |
# A---B---C feature-1
# \
# D---E---F feature-2
git rebase --onto main feature-1 feature-2
# After:
# main
# |
# A---D'---E'---F' feature-2
# |
# B---C feature-1
Bảo toàn sự hợp nhất trong quá trình Rebase
Theo mặc định, việc khởi động lại làm phẳng hợp nhất commit. Để duy trì cấu trúc hợp nhất:
git rebase -i --rebase-merges HEAD~10
Điều này hữu ích khi bạn muốn giữ bối cảnh hợp nhất trong lịch sử của mình.
Ngăn chặn các kỹ thuật nâng cao
Tích trữ các tập tin cụ thể
Để chỉ lưu trữ các tệp cụ thể:
git stash push -m "Work on login component" src/components/Login.js
Tạo một nhánh từ Stash
Nếu bạn muốn áp dụng stash cho một nhánh mới:
git stash branch new-branch stash@{1}
Điều này tạo ra một nhánh mới bắt đầu từ commit nơi stash được tạo, áp dụng stash và loại bỏ nó nếu thành công.
Quản lý nhiều Stash
Khi bạn có nhiều kho lưu trữ, bạn có thể:
Liệt kê tất cả các kho với thông tin chi tiết:
git stash list --stat
Hiển thị nội dung của một stash cụ thể:
git stash show -p stash@{2}
Áp dụng một stash cụ thể mà không xóa nó:
git stash apply stash@{2}
Xóa một kho cụ thể:
git stash drop stash@{2}
Lưu trữ các tập tin không bị theo dõi
Theo mặc định, git stash chỉ lưu các tệp được theo dõi. Để bao gồm các tập tin không bị theo dõi:
git stash -u
Để bao gồm cả các tệp bị bỏ qua:
git stash -a
Mô-đun con và cây con
Làm việc với các mô-đun con
Các mô-đun con cho phép bạn bao gồm các kho lưu trữ Git khác trong kho lưu trữ của mình:
Thêm một mô-đun con:
git submodule add https://github.com/username/library.git lib/library
Khởi tạo các mô-đun con sau khi nhân bản:
git submodule init
git submodule update
Hoặc sao chép với các mô-đun con trong một bước:
git clone --recurse-submodules https://github.com/username/project.git
Cập nhật tất cả các mô-đun con:
git submodule update --remote
Cây con Git
Cây con là một giải pháp thay thế cho các mô-đun con hợp nhất các kho lưu trữ bên ngoài vào một thư mục con:
Thêm một cây con:
git subtree add --prefix=lib/library https://github.com/username/library.git main --squash
Cập nhật cây con:
git subtree pull --prefix=lib/library https://github.com/username/library.git main --squash
Đóng góp trở lại kho lưu trữ ban đầu:
git subtree push --prefix=lib/library https://github.com/username/library.git contribution-branch
Mô-đun con vs Cây con
| tính năng | mô-đun con | Cây con |
|---|---|---|
| Lưu trữ | Chỉ tham khảo | Bản sao đầy đủ của mã |
| Đường cong học tập | Dốc hơn | Nhẹ nhàng hơn |
| Theo dõi sự phụ thuộc | Phiên bản rõ ràng | Có thể ít rõ ràng hơn |
| Cập nhật | Hướng dẫn sử dụng | Có thể dễ dàng hơn |
| Những thay đổi bên ngoài | Cần sự cho phép rõ ràng | Có thể sửa đổi cục bộ |
| Tốt nhất cho | Thư viện của bên thứ ba | Tách dự án |
Viết lại lịch sử
Lọc lịch sử kho lưu trữ
Để viết lại lịch sử mở rộng, hãy sử dụng git-filter-repo (sự thay thế hiện đại cho git-filter-branch):
# Install git-filter-repo
pip install git-filter-repo
# Remove a file from all of history
git filter-repo --path path/to/large-file.bin --invert-paths
Điều này hữu ích cho:
- Xóa các tập tin lớn
- Xóa thông tin nhạy cảm
- Trích xuất thư mục con vào kho lưu trữ riêng của nó
Tách một kho lưu trữ
Để trích xuất thư mục con vào kho lưu trữ mới:
git filter-repo --subdirectory-filter path/to/subdirectory
Điều này viết lại lịch sử như thể thư mục con vốn là thư mục gốc của kho lưu trữ.
Kết hợp kho lưu trữ
Để hợp nhất lịch sử của nhiều kho lưu trữ:
# Add the other repository as a remote
git remote add other-repo https://github.com/username/other-repo.git
git fetch other-repo
# Merge with the allow-unrelated-histories flag
git merge other-repo/main --allow-unrelated-histories
# Resolve any conflicts
git commit
Chiến lược hợp nhất nâng cao
Hợp nhất bạch tuộc
Khi sáp nhập nhiều hơn hai chi nhánh cùng một lúc:
git merge branch1 branch2 branch3
Điều này tạo ra một commit hợp nhất duy nhất với nhiều cha mẹ. Nó chỉ hoạt động nếu không có xung đột.
Hợp nhất với các tùy chọn chiến lược
Git cung cấp một số chiến lược hợp nhất với các tùy chọn có thể tùy chỉnh:
Chiến lược đệ quy với thuật toán kiên nhẫn (xử lý tốt hơn các thay đổi phức tạp):
git merge feature-branch -s recursive -X patience
Bỏ qua các thay đổi về khoảng trắng trong quá trình hợp nhất:
git merge feature-branch -X ignore-space-change
Ưu tiên một bên trong xung đột:
git merge feature-branch -X ours # Keep our version in conflicts
Hoặc:
git merge feature-branch -X theirs # Use their version in conflicts
Ref và Refspecs
Hiểu tài liệu tham khảo Git
Git sử dụng tài liệu tham khảo (refs) để trỏ đến commit:
HEAD: commit hiện tại bạn đang làm việcrefs/heads/main: Nhánh chínhrefs/tags/v1.0.0: Thẻ có tên v1.0.0refs/remotes/origin/main: Nhánh chính trên remote gốc
Bạn có thể sử dụng các tham chiếu tượng trưng để tham khảo commit:
HEAD~3 # Three commits before HEAD
main^2 # The second parent of the main branch tip (for merge commits)
v1.0.0^{} # The commit object that the tag points to
Sử dụng Refspecs cho hoạt động từ xa
Refspecs xác định ánh xạ giữa các tham chiếu từ xa và cục bộ:
git fetch origin +refs/heads/*:refs/remotes/origin/*
Điều này tìm nạp tất cả các nhánh từ nguồn gốc và lưu trữ chúng dưới dạng các nhánh theo dõi từ xa.
Để chỉ tìm nạp các nhánh cụ thể:
git fetch origin main:refs/remotes/origin/main develop:refs/remotes/origin/develop
Tạo tham chiếu tùy chỉnh
Bạn có thể thiết lập các thông số giới thiệu tùy chỉnh trong cấu hình git của mình:
[remote "origin"]
url = https://github.com/username/repo.git
fetch = +refs/heads/*:refs/remotes/origin/*
fetch = +refs/pull/*/head:refs/remotes/origin/pr/*
Với cấu hình này, git fetch cũng sẽ tìm nạp tất cả pull request từ GitHub.
Móc Git
Tạo móc Git hữu ích
Móc Git là các tập lệnh chạy tự động trên các sự kiện Git nhất định. Chúng được lưu trữ trong thư mục .git/hooks.
Móc Pre-commit để ngăn chặn việc cam kết các tệp lớn:
#!/bin/bash
# .git/hooks/pre-commit
# Check for files larger than 5MB
large_files=$(find . -type f -size +5M -not -path "./.git/*")
if [ -n "$large_files" ]; then
echo "Error: Attempting to commit large files:"
echo "$large_files"
echo "Please remove these files or add them to .gitignore"
exit 1
fi
exit 0
Móc commit-msg để thực thi các quy ước thông báo commit:
#!/bin/bash
# .git/hooks/commit-msg
commit_msg_file=$1
commit_msg=$(cat "$commit_msg_file")
# Check if the message follows conventional commits format
if ! echo "$commit_msg" | grep -qE '^(feat|fix|docs|style|refactor|test|chore)(\(.+\))?: .+'; then
echo "Error: Commit message does not follow the conventional commits format."
echo "Example: feat(auth): add login functionality"
exit 1
fi
exit 0
Chia sẻ quan điểm với nhóm của bạn
Móc Git không được sao chép khi kho lưu trữ được sao chép. Để chia sẻ hook:
Lưu trữ hook trong một thư mục trong kho lưu trữ của bạn:
project/ ├── .git/ └── git-hooks/ ├── pre-commit ├── commit-msg └── ...Thiết lập tập lệnh để cài đặt hook:
#!/bin/bash # setup-hooks.sh cp git-hooks/* .git/hooks/ chmod +x .git/hooks/*Ghi lại quá trình trong README của bạn
Ngoài ra, hãy sử dụng công cụ như Husky cho các dự án npm để quản lý hook trong pack.json.
Làm việc với các bản vá
Tạo và áp dụng các bản vá
Các bản vá cho phép bạn chia sẻ các thay đổi mà không cần kho lưu trữ từ xa chung:
Tạo bản vá cho commit cuối cùng:
git format-patch -1 HEAD
Tạo bản vá cho nhiều loại commit:
git format-patch main..feature-branch
Áp dụng một bản vá:
git apply path/to/0001-commit-message.patch
Áp dụng và tạo commit:
git am path/to/0001-commit-message.patch
Tạo chuỗi bản vá
Đối với một loạt các thay đổi liên quan:
git format-patch -o patches/ main..feature-branch --cover-letter
Điều này tạo ra một loạt các tệp vá lỗi có thư xin việc mà bạn có thể chỉnh sửa để giải thích toàn bộ chuỗi.
Thuộc tính Git
Tùy chỉnh hành vi của Git bằng các thuộc tính
Thuộc tính Git, được xác định trong .gitattributes, kiểm soát cách Git xử lý các loại tệp khác nhau:
# .gitattributes
*.txt text
*.jpg binary
*.sh text eol=lf
*.bat text eol=crlf
Điều này đảm bảo:
- Các tệp văn bản có kết thúc dòng được chuẩn hóa
- Tệp JPG được coi là nhị phân
- Các tập lệnh shell luôn sử dụng phần cuối của LF (dành cho Unix)
- Các tệp hàng loạt luôn sử dụng phần cuối CRLF (dành cho Windows)
Xác định các chiến lược khác biệt và hợp nhất tùy chỉnh
Đối với các loại tệp cụ thể:
*.png diff=image
*.docx diff=word
Bạn sẽ cần định cấu hình các trình điều khiển khác biệt này:
git config diff.image.textconv exiftool
git config diff.word.textconv docx2txt
Bây giờ git diff sẽ hiển thị những khác biệt có ý nghĩa đối với các loại tệp này.
Gói và lưu trữ
Tạo kho lưu trữ Git di động
Bundle tạo một tệp duy nhất chứa tất cả commit và các tài liệu tham khảo:
git bundle create repo.bundle HEAD main
Để sao chép từ một gói:
git clone repo.bundle -b main new-repo
Điều này rất hữu ích để truyền dữ liệu Git mà không cần kết nối mạng.
Tạo kho lưu trữ mã của bạn
Để tạo một kho lưu trữ ZIP cho dự án của bạn:
git archive --format=zip HEAD > project.zip
Để lưu trữ một thẻ cụ thể:
git archive --format=zip v1.0.0 > project-v1.0.0.zip
Để bao gồm các mô-đun con:
git submodule foreach --recursive 'git archive --prefix=$path/ --format=zip HEAD > $PWD/submodule-$name.zip'
Cấu hình nâng cao
Định cấu hình Git cho năng suất
Một số cài đặt cấu hình hữu ích:
# Auto-correct typos in Git commands
git config --global help.autocorrect 20
# Show branch names in commit logs
git config --global log.decorate true
# Cache credentials
git config --global credential.helper cache
# Set a global .gitignore
git config --global core.excludesfile ~/.gitignore_global
# Use delta for improved diffs
git config --global core.pager "delta"
# Automatically prune deleted remote branches on fetch/pull
git config --global fetch.prune true
Bí danh cho quy trình làm việc nâng cao
Thiết lập bí danh phức tạp:
# Undo the last commit but keep changes staged
git config --global alias.uncommit 'reset --soft HEAD^'
# Interactive rebase for cleanup
git config --global alias.cleanup 'rebase -i @{upstream}'
# Visualize log with graph
git config --global alias.graph 'log --graph --oneline --decorate --all'
# Show all configs
git config --global alias.aliases 'config --get-regexp ^alias\.'
# Clean up merged branches
git config --global alias.clean-branches '!git branch --merged | grep -v "\*" | xargs -n 1 git branch -d'
Nội bộ Git
Hiểu đối tượng Git
Git lưu trữ bốn loại đối tượng:
- Blob: Nội dung của tệp
- Tree: Danh sách thư mục, chứa các đốm màu và các cây khác
- Commit: Trỏ tới cây, có siêu dữ liệu
- Tag: Một đối tượng trỏ đến một commit cụ thể
Bạn có thể kiểm tra các đối tượng này:
# View a blob
git cat-file -p blob_hash
# View a tree
git cat-file -p tree_hash
# View a commit
git cat-file -p commit_hash
# View an object's type
git cat-file -t object_hash
Tài liệu tham khảo Git
Tài liệu tham khảo (ref) là con trỏ tới commit. Chúng được lưu trữ trong .git/refs/:
- Chi nhánh:
.git/refs/heads/ - Thẻ:
.git/refs/tags/ - Chi nhánh từ xa:
.git/refs/remotes/
Xem commit một điểm tham chiếu đến:
git rev-parse main
git rev-parse HEAD
git rev-parse --verify v1.0.0
Tập lệnh tùy chỉnh sử dụng lệnh hệ thống nước Git
Các lệnh "sửa ống nước" cấp thấp của Git có thể được sử dụng để xây dựng các công cụ tùy chỉnh:
Tập lệnh để tìm các đối tượng lớn trong kho Git của bạn:
#!/bin/bash
# find-large-objects.sh
# Create a temporary pack
git gc --quiet --prune=now
# Find the 10 largest objects
git verify-pack -v .git/objects/pack/*.idx |
grep -v chain |
sort -k3nr |
head -10 |
while read hash type size remainder; do
if [ $size -gt 100000 ]; then
echo "$size bytes: $(git cat-file -t $hash) $hash"
git rev-list --all --objects | grep $hash | sed 's/^.*\t//'
fi
done
Git cho quy trình làm việc chuyên biệt
Monorepos với Git
Để quản lý monorepos lớn:
Sử dụng kiểm tra thưa thớt để làm việc với các tập hợp con của repo:
git clone --no-checkout https://github.com/username/monorepo.git cd monorepo git sparse-checkout set path/to/subdirectory1 path/to/subdirectory2 git checkout mainXem xét việc sao chép một phần để giảm kích thước tải xuống:
git clone --filter=blob:none https://github.com/username/monorepo.gitSử dụng các công cụ như Git LFS cho các tệp nhị phân lớn
Git để tích hợp/triển khai liên tục
Trong CI/CD pipeline, tối ưu hóa hoạt động Git:
Sử dụng bản sao nông để tăng tốc quá trình:
git clone --depth 1 https://github.com/username/repo.gitChỉ lấy các nhánh cần thiết:
git fetch origin $CI_COMMIT_REF_NAMESử dụng chức năng tích hợp của Git để kiểm tra các thay đổi trong các thư mục cụ thể:
if git diff --name-only HEAD~1 HEAD | grep -q "^frontend/"; then run_frontend_tests fi
Git cho Khoa học dữ liệu và ML
Đối với các dự án khoa học dữ liệu:
- Sử dụng Git LFS cho các tệp và bộ dữ liệu mô hình lớn
- Xem xét các công cụ như DVC (Kiểm soát phiên bản dữ liệu) để tạo phiên bản dữ liệu và mô hình ML
- Tạo các hook để ngăn chặn việc chuyển dữ liệu nhạy cảm hoặc các tệp lớn không bị theo dõi
- Sử dụng quy ước đặt tên nhánh phản ánh các thử nghiệm (ví dụ:
experiment/feature-selection-1)
Kết luận
Các kỹ thuật Git nâng cao này cung cấp các công cụ mạnh mẽ để xử lý các tình huống phức tạp và tối ưu hóa quy trình làm việc của bạn. Mặc dù bạn có thể không cần tất cả các tính năng này ngay lập tức nhưng việc hiểu chúng sẽ giúp bạn giải quyết các vấn đề đầy thách thức về kiểm soát phiên bản khi chúng phát sinh.
Hãy nhớ rằng Git rất linh hoạt và có khả năng mở rộng. Khi bạn cảm thấy thoải mái hơn với các tính năng nâng cao này, bạn có thể kết hợp và tùy chỉnh chúng để tạo ra quy trình làm việc hoàn toàn phù hợp với nhu cầu của bạn và của nhóm bạn.
Kỹ năng quan trọng nhất là biết khi nào nên sử dụng những kỹ thuật tiên tiến này. Bắt đầu với những điều cơ bản và dần dần kết hợp các tính năng mạnh mẽ hơn này khi bạn gặp phải những tình huống cần đến chúng. EOF