Làm thế nào người ta có thể thay đổi dấu thời gian của một cam kết cũ trong Git?


747

Các câu trả lời cho Làm thế nào để sửa đổi các cam kết hiện có, chưa được đánh dấu? mô tả một cách để sửa đổi các thông điệp cam kết trước đó chưa được đẩy lên. Các thông báo mới kế thừa dấu thời gian của các cam kết ban đầu. Điều này có vẻ hợp lý, nhưng có cách nào để thiết lập lại thời gian không?



34
git commit --amend --reset-author
Erick M. Sprengel

Câu trả lời:


535

Sử dụng git filter-branchvới bộ lọc env đặt GIT_AUTHOR_DATEGIT_COMMITTER_DATEcho hàm băm cụ thể của cam kết bạn muốn sửa.

Điều này sẽ làm mất hiệu lực điều đó và tất cả các băm trong tương lai.

Thí dụ:

Nếu bạn muốn thay đổi ngày cam kết 119f9ecf58069b265ab22f1f97d2b648faf932e0, bạn có thể làm như vậy với một cái gì đó như thế này:

git filter-branch --env-filter \
    'if [ $GIT_COMMIT = 119f9ecf58069b265ab22f1f97d2b648faf932e0 ]
     then
         export GIT_AUTHOR_DATE="Fri Jan 2 21:38:53 2009 -0800"
         export GIT_COMMITTER_DATE="Sat May 19 01:01:01 2007 -0700"
     fi'


8
Điều đó đã tìm thấy giá trị chính xác, nhưng chỉ cần đặt các biến đó dường như không thực sự ảnh hưởng đến ngày của cam kết cũ.
IQAndreas

36
Ý bạn là gì bởi "Điều này sẽ làm mất hiệu lực điều đó và tất cả các băm trong tương lai."?
EpicDavi

16
EpicDavi: Điều đó có nghĩa là bạn sẽ buộc phải đẩy vào bất kỳ kho lưu trữ từ xa nào và bất kỳ ai đã thực hiện cam kết hoặc bất kỳ cam kết nào trong tương lai sẽ phải đặt lại và kéo, hoặc xóa và sao chép từ đầu. Theo như tôi biết, không có phương pháp nào khắc phục được điều này.
EriF89

4
Cũng giống như một lưu ý cho người mới bắt đầu, hàm băm ngắn không hoạt động trong câu lệnh if, hãy sử dụng SHA-1
40detectives dài

780

Bạn có thể thực hiện một rebase tương tác và chọn chỉnh sửa cho cam kết có ngày bạn muốn thay đổi. Khi quá trình rebase dừng lại để sửa đổi cam kết bạn nhập chẳng hạn:

git commit --amend --date="Wed Feb 16 14:00 2011 +0100"

Sau đó, bạn tiếp tục cuộc nổi loạn tương tác của bạn.

CẬP NHẬT (để phản hồi bình luận của studgeek): để thay đổi ngày cam kết thay vì ngày tác giả:

GIT_COMMITTER_DATE="Wed Feb 16 14:00 2011 +0100" git commit --amend

Các dòng trên đặt biến môi trường GIT_COMMITTER_DATE được sử dụng trong sửa đổi cam kết.

Mọi thứ đều được thử nghiệm trong Git Bash.


22
@nschum --date = "" và --data "văn bản không phải là ngày" đều mang lại kết quả như nhau, lấy ngày của bây giờ.
Paul Pladijs

12
trên phiên bản git 1.7.7.1 bằng cách sử dụng --date = "now" mang lại định dạng ngày không hợp lệ: ngay bây giờ
Aragorn

4
Khi cam kết có ngày bạn muốn thay đổi là cam kết gần đây nhất, bạn không phải thực hiện rebase, bạn chỉ có thể thực hiệngit commit --amend
Eponymous

7
Thay vì xuất GIT_COMMITTER_DATE = "", hãy thử bỏ đặt GIT_COMMITTER_DATE.
Đánh dấu E. Haase

2
Tôi đang sử dụng --no-edit để bạn có thể sử dụng trong các tập lệnh tự động! + var fixedDate = strftime(new Date(), "%c"); + var result = shelljs.exec("git commit --amend --date=\"" + fixedDate + "\" --no-edit");
Marcello de Sales

392

Cách tốt hơn để xử lý tất cả các đề xuất này trong một lệnh là

LC_ALL=C GIT_COMMITTER_DATE="$(date)" git commit --amend --no-edit --date "$(date)"

Điều này sẽ đặt ngày cam kết và tác giả của cam kết cuối cùng thành "ngay bây giờ."


22
Điều này hoạt động tuyệt vời để chỉnh sửa các cam kết cụ thể trong một cuộc nổi loạn tương tác.
Friederbluemle

2
Bạn cũng có thể thêm bí danh vào vỏ cho nó
kaleissin

14
Có vẻ như Git không nhận thức được định dạng ngày, vì vậy để hoàn toàn chính xác, bạn sẽ phải làm cho nó giống như thế này:LANG= GIT_COMMITTER_DATE="`date`" git commit --amend --date "`date`"
Michał Góral

Tôi làm điều đó khi tôi rebase và đè bẹp một nhánh, vì vậy nó tạo ra một cam kết duy nhất với dấu thời gian được cập nhật.
Luke Ehresman

12
bạn cũng có thể chỉ cần làm --date "now". Git> = 2 sẽ diễn giải điều đó.
wvducky

189

Cứ làm đi git commit --amend --reset-author --no-edit. Đối với các xác nhận cũ hơn, bạn có thể thực hiện phản hồi tương tác và chọn editcho cam kết có ngày bạn muốn sửa đổi.

git rebase -i <ref>

Sau đó sửa đổi cam kết với --reset-author--no-editthay đổi ngày tác giả thành ngày hiện tại:

git commit --amend --reset-author --no-edit

Cuối cùng tiếp tục với rebase tương tác của bạn:

git rebase --continue

5
sử dụng tốt --reset-author, nó mới trong git 1.6.6 (ref gitlog.wordpress.com/2010/01/13/git-1-6-6 )
Tim Abell

1
Điều này hoạt động độc đáo để làm cho Github hiển thị các cam kết của một PR bị loại bỏ theo đúng thứ tự, vì họ sắp xếp chúng theo dấu thời gian và không có thủ thuật này, các dấu thời gian có thể giống nhau.
Nathan Long

4
lưu ý --reset-authorsẽ đặt lại cả Tác giả và Ngày tác giả đến bây giờ.
wvducky

điều này sẽ thay đổi "NGÀY CAM KẾT" cùng một lúc?
luochen1990

134

Tôi đã viết một kịch bản và gói Homebrew cho việc này. Siêu dễ cài đặt, bạn có thể tìm thấy nó trên PotatoLabs/git-redatetrang GitHub .

Cú pháp:

git redate -c 3

Bạn chỉ cần chạy git redatevà bạn sẽ có thể chỉnh sửa tất cả các ngày trong vim của 5 lần xác nhận gần đây nhất (cũng có một -ctùy chọn cho bao nhiêu cam kết bạn muốn quay lại, nó chỉ mặc định là 5). Hãy cho tôi biết nếu bạn có bất kỳ câu hỏi, ý kiến, hoặc đề xuất!

nhập mô tả hình ảnh ở đây


2
Những thứ tuyệt vời, mặc dù tôi đã phải sử dụng vim chứ không phải nano
howdoyouturnthison

Cảm ơn @Edmund cho một kịch bản tuyệt vời. Tôi không thể xem ngày để chỉnh sửa trong vi sau khi tôi chạy git redate -c. Tất cả những gì tôi thấy là% cI | XXXXXXXXXXXXXXXX | Cam kết ban đầu. Bạn có thể giúp? Cảm ơn
Kiêm Nguyễn

@KiemNguyen bạn có thể thử chỉ git redate (không có -c) không?
bigpotato

4
hoàn toàn đồng ý với Mina và @howdoyouturnthison ở đây, tại sao bạn không biến nó thành trình soạn thảo bất khả tri thông qua biến môi trường EDITOR? (cũng là tôi trên linux, không phải mac ...)
ympostor

3
Cảm ơn @Edmund! Chỉ trong trường hợp, tập lệnh của bạn có vấn đề với việc xử lý giá trị mặc định cho CAM KẾT. Nếu nó không được đặt, đoạn mã sau sẽ áp dụng các bộ lọc chỉ cho (tôi đoán / tìm thấy) lần xác nhận cuối cùng. "git filter-Branch -f --env-filter" $ ENVFILTER "ĐẦU ~ $ CAM KẾT..IAD> / dev / null"
Entigory Entin

103

Mỗi cam kết được liên kết với hai ngày, ngày bắt đầu và ngày tác giả. Bạn có thể xem những ngày này với:

git log --format=fuller

Nếu bạn muốn thay đổi ngày tác giả và ngày bắt đầu của 6 lần xác nhận gần nhất, bạn chỉ cần sử dụng một rebase tương tác:

git rebase -i HEAD~6

.

pick c95a4b7 Modification 1
pick 1bc0b44 Modification 2
pick de19ad3 Modification 3
pick c110e7e Modification 4
pick 342256c Modification 5
pick 5108205 Modification 6

# Rebase eadedca..5108205 onto eadedca (6 commands)
#
# Commands:
# p, pick = use commit
# r, reword = use commit, but edit the commit message
# e, edit = use commit, but stop for amending
# s, squash = use commit, but meld into previous commit
# f, fixup = like "squash", but discard this commit's log message
# x, exec = run command (the rest of the line) using shell
# d, drop = remove commit

Đối với tất cả các cam kết nơi bạn muốn thay đổi ngày, thay thế pickbằng edit(hoặc chỉ e), sau đó lưu và thoát khỏi trình chỉnh sửa của bạn.

Bây giờ bạn có thể sửa đổi từng cam kết bằng cách chỉ định ngày tác giả và ngày ủy quyền theo định dạng ISO-8601:

GIT_COMMITTER_DATE="2017-10-08T09:51:07" git commit --amend --date="2017-10-08T09:51:07"

Ngày đầu tiên là ngày cam kết, ngày thứ hai là ngày tác giả.

Sau đó đi đến cam kết tiếp theo với:

git rebase --continue

Lặp lại quá trình cho đến khi bạn sửa đổi tất cả các cam kết của bạn. Kiểm tra sự tiến bộ của bạn với git status.


1
Tôi đã làm theo điều này và kết thúc với một "cái đầu bị tháo gỡ"!
Simon H

1
@Simon H Tôi đã kiểm tra lại câu trả lời của tôi và nó hoạt động tốt. Bạn nên gõ một cái gì đó khác nhau hoặc bạn đã ở trong một cái đầu tách ra. Nếu bạn muốn trở về từ một cái đầu tách ra làm a git checkout name-of-current-branch.
Ortomala Lokni

4
Đây là câu trả lời tốt nhất và dễ nhất. Mẹo nhỏ: sử dụng --no-edit trong git commit --amend --no-edit --date=2017-10-08T09:51:07để giữ cũ cam kết thông báo.
Mariusz Pawelski

2
Bạn cũng có thể muốn cập nhật GIT_COMMITTER_DATE như được mô tả ở đây eddmann.com/posts/ Khăn
smihael

2
@smihael Cảm ơn đã liên kết. Tôi đã bao gồm đề nghị của bạn trong câu trả lời của tôi.
Ortomala Lokni


44

Dựa trên câu trả lời của theosp , tôi đã viết một kịch bản có tên (để thay đổi ngày cam kết) mà tôi đưa vào .git-cdcPATH

Tên rất quan trọng: git-xxxbất cứ nơi nào trong bạn PATHcho phép bạn nhập:

git xxx
# here
git cdc ... 

Kịch bản đó bị lỗi, ngay cả trên Windows (vì Git sẽ gọi nó từ môi trường msys của nó )

#!/bin/bash
# commit
# date YYYY-mm-dd HH:MM:SS

commit="$1" datecal="$2"
temp_branch="temp-rebasing-branch"
current_branch="$(git rev-parse --abbrev-ref HEAD)"

date_timestamp=$(date -d "$datecal" +%s)
date_r=$(date -R -d "$datecal")

if [[ -z "$commit" ]]; then
    exit 0
fi

git checkout -b "$temp_branch" "$commit"
GIT_COMMITTER_DATE="$date_timestamp" GIT_AUTHOR_DATE="$date_timestamp" git commit --amend --no-edit --date "$date_r"
git checkout "$current_branch"
git rebase  --autostash --committer-date-is-author-date "$commit" --onto "$temp_branch"
git branch -d "$temp_branch"

Với điều đó, bạn có thể gõ:

git cdc @~ "2014-07-04 20:32:45"

Điều đó sẽ đặt lại ngày tác giả / cam kết của cam kết trước HEAD ( @~) thành ngày đã chỉ định.

git cdc @~ "2 days ago"

Điều đó sẽ đặt lại ngày tác giả / cam kết của cam kết trước HEAD ( @~) thành cùng giờ, nhưng 2 ngày trước.


Ilya Semenov đề cập trong các ý kiến :

Đối với OS X, bạn cũng có thể cài đặt GNU coreutils( brew install coreutils), thêm nó vàoPATH ( PATH="/usr/local/opt/coreutils/libexec/gnubin:$PATH") và sau đó sử dụng 2 days agocú pháp "".


1
Đối với tôi, điều này chỉ hoạt động với việc trích dẫn ngày và thời gian thành một trích dẫn: git cdc @~ "2014-07-04 20:32:45nếu không, nó sẽ không nhận ra thời gian và do đó có được thời gian 00:00:00 (nó trở thành đối số thứ ba).
peschü

3
Đối với OS X, bạn cũng có thể cài đặt GNU coreutils ( brew install coreutils), thêm nó vào PATH ( PATH="/usr/local/opt/coreutils/libexec/gnubin:$PATH") và sau đó sử dụng cú pháp "2 ngày trước".
Ilya Semenov

1
@IlyaSemenov Thú vị. Tôi đã bao gồm nhận xét của bạn trong câu trả lời để dễ nhìn hơn.
VonC

Tôi đang cố gắng sử dụng ví dụ đầu tiên của bạn nhưng tôi vẫn nhận được "định dạng ngày không hợp lệ:". Định dạng ngày nào là Mac OS X mong đợi?
usbsnowcrash

@usbsnowcrash không chắc chắn trên mac. Ví dụ thứ hai " 2 days ago" có hoạt động không?
VonC

25

Cách chỉnh sửa nhiều ngày cam kết

Các câu trả lời khác không thuận tiện cho việc chỉnh sửa một số ngày cam kết. Tôi đã trở lại câu hỏi này sau một vài năm để chia sẻ một kỹ thuật.

Để thay đổi ngày của 4 lần xác nhận gần nhất:

git rebase -i HEAD~4

Chỉnh sửa rebase như sau, chèn execcác dòng để sửa đổi ngày nếu cần:

pick 4ca564e Do something
exec git commit --amend --no-edit --date "1 Oct 2019 12:00:00 PDT"
pick 1670583 Add another thing
exec git commit --amend --no-edit --date "2 Oct 2019 12:00:00 PDT"
pick b54021c Add some tests
exec git commit --amend --no-edit --date "3 Oct 2019 12:00:00 PDT"
pick e8f6653 Fix the broken thing
exec git commit --amend --no-edit --date "4 Oct 2019 12:00:00 PDT"

Sử dụng tốt các tùy chọn --amend/ --date. Đơn giản hơn câu trả lời của riêng tôi bằng cách sử dụng các biến môi trường. Nâng cao.
VonC

Có thể sử dụng ngày / giờ hiện tại làm tham số không?
accfews

GIT_AUTHOR_DATEchỉ cập nhật thôi.
Blaise

Re. 'Có thể sử dụng ngày / giờ hiện tại làm tham số không?': "Bây giờ" được hiểu là ngày hợp lệ, vì vậy các dòng thực thi ở trên sẽ trở thànhexec git commit --amend --no-edit --date "now"
Andrew Richards

20

nếu đó là cam kết cuối cùng trước đó.

git rebase  -i HEAD~2
git commit --amend --date=now

nếu bạn đã đẩy lên quỹ đạo và có thể buộc sử dụng:

git push --force 

nếu bạn không thể ép buộc và nếu nó bị đẩy, bạn không thể thay đổi cam kết! .


18

Đây là một bí danh thuận tiện thay đổi cả thời gian cam kết và tác giả của lần cam kết cuối cùng thành thời gian được chấp nhận bởi date --date:

[alias]
    cd = "!d=\"$(date -d \"$1\")\" && shift && GIT_COMMITTER_DATE=\"$d\" \
            git commit --amend --date \"$d\""

Sử dụng: git cd <date_arg>

Ví dụ:

git cd now  # update the last commit time to current time
git cd '1 hour ago'  # set time to 1 hour ago

Chỉnh sửa: Đây là phiên bản tự động hơn để kiểm tra xem chỉ mục có sạch không (không có thay đổi không được cam kết) và sử dụng lại thông báo cam kết cuối cùng, hoặc không thành công (chứng minh đánh lừa):

[alias]
    cd = "!d=\"$(date -d \"$1\")\" && shift && \
        git diff-index --cached --quiet HEAD --ignore-submodules -- && \
        GIT_COMMITTER_DATE=\"$d\" git commit --amend -C HEAD --date \"$d\"" \
        || echo >&2 "error: date change failed: index not clean!"

17

Tôi đã tạo gói npm này để thay đổi ngày của các cam kết cũ.

https://github.com/bitriddler/git-change-date

Sử dụng mẫu:

npm install -g git-change-date
cd [your-directory]
git-change-date

Bạn sẽ được nhắc chọn cam kết bạn muốn sửa đổi sau đó nhập ngày mới.

Nếu bạn muốn thay đổi một cam kết bằng hàm băm cụ thể, hãy chạy nó git-change-date --hash=[hash]


Tôi chỉ muốn nói rằng điều này là tuyệt vời và làm việc tốt đẹp. Cảm ơn bạn, bạn đã tiết kiệm cho tôi rất nhiều thời gian!
paranza

17

Hàm bash sau đây sẽ thay đổi thời gian của bất kỳ cam kết nào trên nhánh hiện tại.

Cẩn thận không sử dụng nếu bạn đã đẩy cam kết hoặc nếu bạn sử dụng cam kết trong một chi nhánh khác.

# rewrite_commit_date(commit, date_timestamp)
#
# !! Commit has to be on the current branch, and only on the current branch !!
# 
# Usage example:
#
# 1. Set commit 0c935403 date to now:
#
#   rewrite_commit_date 0c935403
#
# 2. Set commit 0c935403 date to 1402221655:
#
#   rewrite_commit_date 0c935403 1402221655
#
rewrite_commit_date () {
    local commit="$1" date_timestamp="$2"
    local date temp_branch="temp-rebasing-branch"
    local current_branch="$(git rev-parse --abbrev-ref HEAD)"

    if [[ -z "$date_timestamp" ]]; then
        date="$(date -R)"
    else
        date="$(date -R --date "@$date_timestamp")"
    fi

    git checkout -b "$temp_branch" "$commit"
    GIT_COMMITTER_DATE="$date" git commit --amend --date "$date"
    git checkout "$current_branch"
    git rebase "$commit" --onto "$temp_branch"
    git branch -d "$temp_branch"
}

1
Bạn có một lỗi trong đó: if [[ -z "$commit" ]]->if [[ -z "$date_timestamp" ]]
blueFast

Đẹp! Tôi sẽ khuyên bạn nên cài đặt GIT_COMMITTER_DATE=ở cuối phương thức để ngăn chặn mọi cam kết thủ công hơn nữa để giữ ngày được chỉ định.
loopkin

@loopkin, GIT_COMMITTER_DATE được đặt chỉ cho lệnh "git commit" vì vậy không cần phải xóa nó sau đó
nimrodm

@nimrodm, tôi mới kiểm tra lại và bạn đã đúng. Cảm ơn đã chỉ ra rằng.
loopkin

12

Để thay đổi cả ngày tác giả và ngày cam kết:

GIT_COMMITTER_DATE="Wed Sep 23 9:40 2015 +0200" git commit --amend --date "Wed Sep 23 9:40 2015 +0200"

10

Nếu bạn muốn có được ngày chính xác của một cam kết khác (giả sử bạn rebase đã chỉnh sửa một cam kết và muốn nó có ngày của phiên bản pre-rebase gốc):

git commit --amend --date="$(git show -s --format=%ai a383243)"

Điều này sửa ngày cam kết của CHÍNH là chính xác ngày cam kết a383243 (bao gồm nhiều chữ số hơn nếu có sự mơ hồ). Nó cũng sẽ bật lên một cửa sổ soạn thảo để bạn có thể chỉnh sửa thông điệp cam kết.

Đó là ngày của tác giả, đó là điều bạn thường quan tâm - xem các câu trả lời khác cho ngày bắt đầu.


7

Nếu bạn muốn thực hiện câu trả lời được chấp nhận ( https://stackoverflow.com/a/454750/72809 ) trong dòng lệnh Windows tiêu chuẩn, bạn cần có lệnh sau:

git filter-branch -f --env-filter "if [ $GIT_COMMIT = 578e6a450ff5318981367fe1f6f2390ce60ee045 ]; then export GIT_AUTHOR_DATE='2009-10-16T16:00+03:00'; export GIT_COMMITTER_DATE=$GIT_AUTHOR_DATE; fi"

Ghi chú:

  • Có thể phân chia lệnh trên nhiều dòng (Windows hỗ trợ phân tách dòng bằng biểu tượng carret ^ ), nhưng tôi đã không thành công.
  • Bạn có thể viết ngày ISO, tiết kiệm rất nhiều thời gian để tìm đúng ngày trong tuần và sự thất vọng chung về thứ tự các yếu tố.
  • Nếu bạn muốn ngày của Tác giả và Người ủy quyền giống nhau, bạn chỉ có thể tham chiếu biến đã đặt trước đó.

Rất cám ơn đến một bài viết trên blog của Colin Svingen . Mặc dù mã của anh ấy không hoạt động với tôi, nhưng nó đã giúp tôi tìm ra giải pháp chính xác.


7

Nếu cam kết chưa được đẩy thì tôi có thể sử dụng một cái gì đó như thế:

git commit --amend --date=" Wed Mar 25 10:05:44 2020 +0300"

sau đó git bash mở trình soạn thảo với ngày đã được áp dụng, do đó bạn chỉ cần lưu nó bằng cách nhập vào chế độ lệnh của trình soạn thảo VI ": wq" và bạn có thể đẩy nó


2
Chỉ cần thêm vào câu trả lời hay: nếu bạn không muốn chỉnh sửa thông điệp cam kết (nếu bạn chỉ muốn thay đổi ngày cam kết), hãy sử dụng --no-edittùy chọn.
Antonio Vinicius Menezes Medei

Ngoài ra, nếu cam kết đã được đẩy, bạn vẫn có thể đẩy cam kết đã thay đổi bằng cách sử dụng git push -f(cập nhật bắt buộc). Điều đó có thể có tác dụng phụ, mặc dù. (đặc biệt là nếu nhiều người có bản sao cục bộ của kho lưu trữ)
Antonio Vinicius Menezes Medei


2

Đã có nhiều câu trả lời tuyệt vời, nhưng khi tôi muốn thay đổi ngày cho nhiều lần xác nhận trong một ngày hoặc trong một tháng, tôi không tìm thấy câu trả lời thích hợp. Vì vậy, tôi tạo ra một kịch bản mới cho điều này với sự khám phá, hy vọng nó sẽ giúp được ai đó:

#!/bin/bash

# change GIT_AUTHOR_DATE for commit at Thu Sep 14 13:39:41 2017 +0800
# you can change the data_match to change all commits at any date, one day or one month
# you can also do the same for GIT_COMMITTER_DATE

git filter-branch --force --env-filter '

date_match="^Thu, 14 Sep 2017 13+"              

# GIT_AUTHOR_DATE will be @1505367581 +0800, Git internal format 
author_data=$GIT_AUTHOR_DATE;                   
author_data=${author_data#@}                  
author_data=${author_data% +0800}                # author_data is 1505367581     

oneday=$((24*60*60))

# author_data_str will be "Thu, 14 Sep 2017 13:39:41 +0800", RFC2822 format
author_data_str=`date -R -d @$author_data`      

if [[ $author_data_str =~ $date_match ]];
then
    # remove one day from author_data
    new_data_sec=$(($author_data-$oneday))
    # change to git internal format based on new_data_sec
    new_data="@$new_data_sec +0800"             
    export GIT_AUTHOR_DATE="$new_data"
fi
' --tag-name-filter cat -- --branches --tags

Ngày sẽ được thay đổi:

AuthorDate: Wed Sep 13 13:39:41 2017 +0800
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.