Sử dụng thực tế của git reset --soft?


136

Tôi đã làm việc với git chỉ hơn một tháng. Quả thực tôi đã sử dụng thiết lập lại lần đầu tiên chỉ ngày hôm qua, nhưng thiết lập lại mềm vẫn không có ý nghĩa nhiều với tôi.

Tôi hiểu rằng tôi có thể sử dụng thiết lập lại mềm để chỉnh sửa một cam kết mà không thay đổi chỉ mục hoặc thư mục làm việc, như tôi đã làm với git commit --amend.

Có phải hai lệnh này thực sự giống nhau ( reset --softvs commit --amend)? Bất kỳ lý do để sử dụng một hoặc khác trong điều khoản thực tế? Và quan trọng hơn, có cách sử dụng nào khác reset --softngoài việc sửa đổi một cam kết không?

Câu trả lời:


110

git resetlà tất cả về di chuyển HEAD, và nói chung là chi nhánh ref .
Câu hỏi: những gì về cây làm việc và chỉ số?
Khi làm việc với --soft, di chuyển HEAD, thường xuyên cập nhật các chi nhánh ref và chỉHEAD .
Điều này khác commit --amendvới:

  • nó không tạo ra một cam kết mới.
  • nó thực sự có thể di chuyển CHÍNH đến bất kỳ cam kết nào ( commit --amendchỉ là về việc không di chuyển CHÍNH, trong khi cho phép làm lại cam kết hiện tại)

Chỉ cần tìm ví dụ này về sự kết hợp:

  • một sự hợp nhất cổ điển
  • hợp nhất cây con

tất cả thành một (bạch tuộc, vì có nhiều hơn hai nhánh được hợp nhất) cam kết hợp nhất.

Tomas "wasroulette" Carnecky giải thích trong bài viết "Subtree Octopus merge" của mình :

  • Chiến lược hợp nhất cây con có thể được sử dụng nếu bạn muốn hợp nhất một dự án vào thư mục con của dự án khác và sau đó giữ cho dự án con được cập nhật. Nó là một thay thế cho mô hình con git.
  • Chiến lược hợp nhất bạch tuộc có thể được sử dụng để hợp nhất ba hoặc nhiều nhánh. Chiến lược bình thường chỉ có thể hợp nhất hai nhánh và nếu bạn cố gắng hợp nhất nhiều hơn thế, git sẽ tự động quay trở lại chiến lược bạch tuộc.

Vấn đề là bạn chỉ có thể chọn một chiến lược. Nhưng tôi muốn kết hợp cả hai để có được một lịch sử rõ ràng trong đó toàn bộ kho được cập nhật nguyên bản lên một phiên bản mới.

Tôi có một siêu projectAdự án , hãy gọi nó và một tiểu dự án projectB, mà tôi đã hợp nhất thành một thư mục con của projectA.

(đó là phần hợp nhất của cây con)

Tôi cũng đang duy trì một vài cam kết địa phương.
ProjectAđược cập nhật thường xuyên, projectBcó phiên bản mới mỗi vài ngày hoặc vài tuần và thường phụ thuộc vào một phiên bản cụ thể của projectA.

Khi tôi quyết định cập nhật cả hai dự án, tôi không chỉ đơn giản rút ra projectAprojectB vì điều đó sẽ tạo ra hai cam kết cho những gì nên là một bản cập nhật nguyên tử của toàn bộ dự án .
Thay vào đó, tôi tạo ra một hợp nhất cam kết duy nhất mà liên hợp gặt đập projectA, projectBvà địa phương cam kết của tôi .
Phần khó khăn ở đây là đây là sự hợp nhất của bạch tuộc (ba đầu), nhưng projectBcần phải được hợp nhất với chiến lược cây con . Vì vậy, đây là những gì tôi làm:

# Merge projectA with the default strategy:
git merge projectA/master

# Merge projectB with the subtree strategy:
git merge -s subtree projectB/master

Ở đây, tác giả đã sử dụng một reset --hard, và sau đó read-treeđể khôi phục lại hai lần hợp nhất đầu tiên đã thực hiện cho cây và chỉ mục, nhưng đó là nơi reset --softcó thể giúp:
Làm thế nào để tôi làm lại hai sự hợp nhất đó, đã hoạt động, tức là cây và chỉ mục của tôi tốt, nhưng không phải ghi lại hai cam kết?

# Move the HEAD, and just the HEAD, two commits back!
git reset --soft HEAD@{2}

Bây giờ, chúng ta có thể tiếp tục giải pháp của Tomas:

# Pretend that we just did an octopus merge with three heads:
echo $(git rev-parse projectA/master) > .git/MERGE_HEAD
echo $(git rev-parse projectB/master) >> .git/MERGE_HEAD

# And finally do the commit:
git commit

Vì vậy, mỗi lần:

  • bạn hài lòng với những gì bạn kết thúc (về cây và chỉ số làm việc)
  • bạn không hài lòng với tất cả các cam kết đưa bạn đến đó:

git reset --soft là câu trả lời.


8
Lưu ý đến bản thân: ví dụ đơn giản về việc bẹp với git reset --soft: stackoverflow.com/questions/6869705/
Kẻ

3
Nó cũng hữu ích nếu bạn cam kết với chi nhánh sai. tất cả các thay đổi quay trở lại khu vực tổ chức và di chuyển với bạn trong khi bạn kiểm tra chi nhánh bên phải.
ai đó

44

Ca sử dụng - Kết hợp một loạt các cam kết cục bộ

"Rất tiếc. Ba cam kết đó có thể chỉ là một."

Vì vậy, hoàn tác 3 lần cuối (hoặc bất cứ điều gì) cam kết (mà không ảnh hưởng đến chỉ mục cũng như thư mục làm việc). Sau đó cam kết tất cả các thay đổi là một.

Ví dụ

> git add -A; git commit -m "Start here."
> git add -A; git commit -m "One"
> git add -A; git commit -m "Two"
> git add -A' git commit -m "Three"
> git log --oneline --graph -4 --decorate

> * da883dc (HEAD, master) Three
> * 92d3eb7 Two
> * c6e82d3 One
> * e1e8042 Start here.

> git reset --soft HEAD~3
> git log --oneline --graph -1 --decorate

> * e1e8042 Start here.

Bây giờ tất cả các thay đổi của bạn được bảo tồn và sẵn sàng để được cam kết là một.

Câu trả lời ngắn cho câu hỏi của bạn

Có phải hai lệnh này thực sự giống nhau ( reset --softvs commit --amend)?

  • Không.

Bất kỳ lý do để sử dụng một hoặc khác trong điều khoản thực tế?

  • commit --amend để thêm / rm tệp từ lần xác nhận cuối cùng hoặc thay đổi thông báo của nó.
  • reset --soft <commit> để kết hợp một số cam kết tuần tự thành một cam kết mới.

Và quan trọng hơn, có cách sử dụng nào khác reset --softngoài việc sửa đổi một cam kết không?

  • Xem câu trả lời khác :)

2
Xem các câu trả lời khác cho "có bất kỳ cách sử dụng nào khác reset --softngoài việc sửa đổi một cam kết - Không"
Antony Hatchkins

Để đối phó với những nhận xét cuối cùng - đã đồng ý một phần, nhưng tôi muốn nói rằng việc sửa đổi một đơn cam kết là quá cụ thể. Chẳng hạn, bạn có thể muốn, một khi một tính năng được viết, xóa sạch mọi thứ và thực hiện các cam kết mới hữu ích hơn cho người đánh giá. Tôi muốn nói rằng các thiết lập lại mềm (bao gồm cả đơn giản git reset) là tốt khi bạn (a) muốn viết lại lịch sử, (b) không quan tâm đến các cam kết cũ (vì vậy bạn có thể tránh sự ồn ào của rebase tương tác) và (c) có nhiều hơn một cam kết thay đổi (nếu không, commit --amendđơn giản hơn).
johncip

18

Tôi sử dụng nó để sửa đổi nhiều hơn chỉ là cam kết cuối cùng .

Giả sử tôi đã phạm sai lầm trong cam kết A và sau đó thực hiện cam kết B. Bây giờ tôi chỉ có thể sửa đổi B. Vì vậy, tôi làm git reset --soft HEAD^^ , tôi sửa và cam kết lại A và sau đó cam kết lại B.

Tất nhiên, nó không thuận tiện lắm cho các cam kết lớn nhưng dù sao bạn cũng không nên thực hiện các cam kết lớn ;-)


3
git commit --fixup HEAD^^ git rebase --autosquash HEAD~Xhoạt động tốt, quá.
Niklas

3
git rebase --interactive HEAD^^trong đó bạn chọn chỉnh sửa cả hai cam kết A và B. Cách này giữ các thông điệp cam kết của A và B, nếu cần bạn vẫn có thể sửa đổi các cam kết đó.
Paul Pladijs

2
Làm cách nào để tôi tái cam kết B sau khi tôi đặt lại thành A?
Northben

1
Một cách khác có lẽ ít hoạt động hơn: kiểm tra nhật ký git của bạn, lấy hàm băm cam kết của B, sau đó git reset A, thực hiện và thêm các thay đổi git commit --amend, git cherry-pick <B-commit-hash>.
ness101

15

Một cách sử dụng tiềm năng khác là một cách thay thế cho stashing (điều mà một số người không thích, xem ví dụ https://codingkillsthecat.wordpress.com/2012/04/27/git-stash-pop-considered-harmful/ ).

Ví dụ: nếu tôi đang làm việc trên một chi nhánh và cần sửa chữa một cái gì đó khẩn cấp trên chủ, tôi chỉ có thể làm:

git commit -am "In progress."

sau đó kiểm tra tổng thể và làm các sửa chữa. Khi tôi hoàn thành, tôi trở về chi nhánh của mình và làm

git reset --soft HEAD~1

để tiếp tục làm việc nơi tôi rời đi.


3
Cập nhật (bây giờ tôi hiểu git tốt hơn): --softthực sự không cần thiết ở đây, trừ khi bạn thực sự quan tâm đến việc thay đổi sẽ được tổ chức ngay lập tức. Bây giờ tôi chỉ sử dụng git reset HEAD~khi làm điều này. Nếu tôi có một số thay đổi được dàn dựng khi tôi cần chuyển nhánh, và muốn giữ nó theo cách đó, thì tôi sẽ làm git commit -m "staged changes", sau đó git commit -am "unstaged changes", sau đó git reset HEAD~tiếp theo git reset --soft HEAD~để khôi phục hoàn toàn trạng thái làm việc. Mặc dù, thành thật mà nói tôi làm cả hai điều này ít hơn rất nhiều bây giờ tôi biết về git-worktree:)
deltacrux

7

Bạn có thể dùng git reset --soft để thay đổi phiên bản bạn muốn có với tư cách là cha mẹ cho những thay đổi bạn có trong chỉ mục và cây làm việc của bạn. Các trường hợp này là hữu ích là rất hiếm. Đôi khi bạn có thể quyết định rằng những thay đổi bạn có trong cây làm việc của bạn nên thuộc về một nhánh khác. Hoặc bạn có thể sử dụng điều này như một cách đơn giản để thu gọn một số xác nhận thành một (tương tự như squash / Fold).

Xem câu trả lời này của VonC để biết ví dụ thực tế: Bóp hai lần cam kết đầu tiên trong Git?


Đó là một câu hỏi hay mà tôi chưa tìm thấy trước đây. Nhưng tôi không chắc mình có thể thấy cách đặt thay đổi lên một nhánh khác bằng cách sử dụng thiết lập lại mềm. Vậy tôi có Chi nhánh thanh toán, rồi tôi dùng git reset --soft anotherBranchrồi cam kết ở đó? Nhưng bạn không thực sự thay đổi chi nhánh ckeckout, vì vậy bạn sẽ cam kết với Chi nhánh hoặc cho một Công ty khác ?
AJJ

Khi làm điều này, điều quan trọng là bạn không sử dụng kiểm tra git vì điều đó sẽ thay đổi cây của bạn. Hãy nghĩ về git reset --soft như một cách để thao túng các điểm CHÍNH sửa đổi.
Julian Rudolph

7

Một cách sử dụng có thể là khi bạn muốn tiếp tục công việc của mình trên một máy khác. Nó sẽ hoạt động như thế này:

  1. Kiểm tra một chi nhánh mới với một tên giống như stash,

    git checkout -b <branchname>_stash
    
  2. Đẩy nhánh stash của bạn lên,

    git push -u origin <branchname>_stash
    
  3. Chuyển sang máy khác của bạn.

  4. Kéo xuống cả stash của bạn và các nhánh hiện có,

    git checkout <branchname>_stash; git checkout <branchname>
    
  5. Bạn nên ở trên chi nhánh hiện tại của bạn bây giờ. Hợp nhất trong các thay đổi từ nhánh stash,

    git merge <branchname>_stash
    
  6. Soft đặt lại chi nhánh hiện tại của bạn thành 1 trước khi hợp nhất,

    git reset --soft HEAD^
    
  7. Loại bỏ nhánh stash của bạn,

    git branch -d <branchname>_stash
    
  8. Cũng loại bỏ nhánh stash của bạn từ nguồn gốc,

    git push origin :<branchname>_stash
    
  9. Tiếp tục làm việc với các thay đổi của bạn như thể bạn đã xử lý chúng bình thường.

Tôi nghĩ, trong tương lai, GitHub và co. sẽ cung cấp chức năng "stash từ xa" này trong ít bước hơn.


2
Tôi muốn chỉ ra rằng stash và pop đầu tiên trên máy đầu tiên của bạn là hoàn toàn không cần thiết, bạn chỉ có thể tạo một nhánh mới trực tiếp từ một bản sao làm việc bẩn, cam kết, sau đó đẩy các thay đổi lên từ xa.

7

Một cách sử dụng thực tế là nếu bạn đã cam kết với repo cục bộ của mình (ví dụ: git commit -m) thì bạn có thể đảo ngược cam kết cuối cùng đó bằng cách thực hiện git reset --soft HEAD ~ 1

Ngoài ra, đối với kiến ​​thức của bạn, nếu bạn đã dàn dựng các thay đổi của mình (ví dụ như với git add.) Thì bạn có thể đảo ngược việc dàn dựng bằng cách thực hiện git reset - trộn lẫn ĐẦU hoặc tôi thường chỉ sử dụnggit reset

cuối cùng, git reset - xóa sạch mọi thứ kể cả những thay đổi cục bộ của bạn. Đầu ~ sau cho bạn biết có bao nhiêu cam kết đi từ đầu.


1
git reset --soft HEAD ~1mang lại cho tôi fatal: Cannot do soft reset with paths.Tôi nghĩ rằng chúng ta cần phải xóa khoảng trống sau CHÍNH vì vậy nó sẽ làgit reset --soft HEAD~1
Andrew Lohr

6

Một lý do tuyệt vời để sử dụng 'git reset --soft <sha1> ' là để di chuyểnHEAD trong một repo trần.

Nếu bạn cố gắng sử dụng --mixed hoặc--hard tùy chọn, bạn sẽ gặp lỗi do bạn đang cố gắng sửa đổi và làm việc cây và / hoặc chỉ mục không tồn tại.

Lưu ý: Bạn sẽ cần phải làm điều này trực tiếp từ repo trần.

Lưu ý một lần nữa: Bạn sẽ cần đảm bảo rằng nhánh bạn muốn đặt lại trong repo trần là nhánh hoạt động. Nếu không, hãy làm theo câu trả lời của VonC về cách cập nhật nhánh hoạt động trong một repo trần khi bạn có quyền truy cập trực tiếp vào repo.


1
Như đã đề cập trong câu trả lời trước của bạn ( stackoverflow.com/questions/4624881/ ,), điều này đúng khi bạn có quyền truy cập trực tiếp vào repo trần (mà bạn đề cập ở đây). +1 mặc dù.
VonC

@VonC Vâng, hoàn toàn chính xác và cảm ơn vì đã thêm ghi chú! Tôi tiếp tục quên thêm rằng vì tôi cho rằng việc đặt lại được thực hiện trực tiếp từ repo. Ngoài ra, tôi cho rằng nhánh mà người đó muốn đặt lại trong repo trần là nhánh hoạt động của họ. Nếu chi nhánh không phải là chi nhánh hoạt động của họ, thì cần phải cập nhật theo câu trả lời của bạn ( stackoverflow.com/questions/3301956/ Ấn ) về cách cập nhật chi nhánh hoạt động cho repos trần. Tôi cũng sẽ cập nhật câu trả lời với thông tin chi nhánh đang hoạt động. Cảm ơn một lần nữa !!!
Hazok

2

SourceTree là một git GUI có giao diện khá thuận tiện để chỉ dàn các bit bạn muốn. Nó không có bất cứ điều gì tương tự từ xa để sửa đổi một sửa đổi thích hợp.

Vì vậy, git reset --soft HEAD~1hữu ích hơn nhiềucommit --amend trong kịch bản này. Tôi có thể hoàn tác cam kết, đưa tất cả các thay đổi trở lại khu vực tổ chức và tiếp tục điều chỉnh các bit được phân đoạn bằng SourceTree.

Thực sự, có vẻ như đó commit --amendlà mệnh lệnh dư thừa của cả hai, nhưng git là git và không né tránh các lệnh tương tự làm những việc hơi khác nhau.


1

Trong khi tôi thực sự thích câu trả lời trong chủ đề này, tôi sử dụng git reset --soft cho một chút khác biệt, tuy nhiên một kịch bản rất thực tế.

Tôi sử dụng IDE để phát triển có công cụ tìm khác biệt tốt để hiển thị các thay đổi (được dàn dựng và không được dàn dựng) sau lần cam kết cuối cùng của tôi. Bây giờ, hầu hết các nhiệm vụ của tôi liên quan đến nhiều cam kết. Ví dụ: giả sử tôi thực hiện 5 cam kết để hoàn thành một nhiệm vụ cụ thể. Tôi sử dụng công cụ tìm khác biệt trong IDE trong mỗi lần xác nhận tăng dần từ 1-5 để xem xét các thay đổi của tôi từ lần xác nhận cuối cùng. Tôi thấy đó là một cách rất hữu ích để xem xét các thay đổi của mình trước khi cam kết.

Nhưng khi kết thúc nhiệm vụ của mình, khi tôi muốn xem tất cả các thay đổi của mình (từ trước lần cam kết đầu tiên), để tự kiểm tra mã trước khi thực hiện yêu cầu kéo, tôi sẽ chỉ thấy các thay đổi từ cam kết trước đó (sau khi cam kết 4) và không thay đổi từ tất cả các cam kết của nhiệm vụ hiện tại của tôi.

Vì vậy, tôi sử dụng git reset --soft HEAD~4để quay trở lại 4 cam kết. Điều này cho phép tôi thấy tất cả các thay đổi cùng nhau. Khi tôi tự tin về những thay đổi của mình, tôi có thể làm git reset HEAD@{1}và đẩy nó từ xa một cách tự tin.


1
... sau đó làm git add --patchgit commitlặp đi lặp lại để xây dựng chuỗi cam kết mà bạn đã xây dựng nếu bạn biết tất cả những gì bạn đang làm. Cam kết đầu tiên của bạn giống như các ghi chú trên bàn của bạn hoặc bản thảo đầu tiên của bản ghi nhớ, chúng ở đó để sắp xếp suy nghĩ của bạn, không phải để xuất bản.
jthill

Hey, tôi đã không nhận được những gì bạn đang đề nghị.
Swanky

1
Chỉ là thay vì làm git reset @{1}để khôi phục loạt dự thảo đầu tiên của bạn, thay vào đó bạn có thể xây dựng một loạt cho xuất bản từ git add -pgit commit.
jthill

Vâng chính xác. Một cách khác là (cách tôi thường làm theo) để nổi loạn trên thượng nguồn / chủ và ép các cam kết thành một.
Swanky

1

Một trường hợp sử dụng khác là khi bạn muốn thay thế nhánh khác bằng nhánh của bạn trong yêu cầu kéo, ví dụ, giả sử rằng bạn có một phần mềm có các tính năng A, B, C đang phát triển.

Bạn đang phát triển với phiên bản tiếp theo và bạn:

  • Đã xóa tính năng B

  • Đã thêm tính năng D

Trong quá trình, phát triển chỉ cần thêm hotfix cho tính năng B.

Bạn có thể hợp nhất phát triển thành tiếp theo, nhưng đôi khi có thể lộn xộn, nhưng bạn cũng có thể sử dụng git reset --soft origin/developvà tạo một cam kết với các thay đổi của mình và chi nhánh có thể được hợp nhất mà không có xung đột và giữ các thay đổi của bạn.

Hóa ra đó git reset --softlà một lệnh tiện dụng. Cá nhân tôi sử dụng nó rất nhiều để xóa các cam kết không có "công việc đã hoàn thành" như "WIP" vì vậy khi tôi mở yêu cầu kéo, tất cả các cam kết của tôi đều có thể hiểu được.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.