3 # Restore files in the working tree to the given state
4 # Copyright (c) Petr Baudis, 2005
6 # Restore given files to their original state. It recovers any files
7 # (or files passed as arguments to the command, respectively) removed
8 # locally whose removal was not recorded by `cg-rm`.
10 # If passed the -f parameter, it restores the files to their state
11 # as of the last commit (including bringing files removed with
12 # `cg-rm` back to life).
14 # If passed the -r parameter, it will not restore the file as of the
15 # last commit, but to the state in the given commit, tree, or blob.
16 # The list of files is mandatory in this case.
18 # For the "restore-to-last-commit" usage, this command is
19 # complementary to the `cg-reset` command, which forcefully abandons
20 # all the changes in the working tree and restores everything to
21 # a proper state (including unseeking, cancelling merge in progress
22 # and rebuilding indexes).
26 # -f:: Undo local changes since last commit
27 # Restore even locally modified files to the version as of
28 # the last commit. Take care!
30 # -r COMMIT_ID:: Restore files to given COMMIT_ID
31 # -r TREE_ID:: Restore files to given TREE_ID
32 # -r BLOB_ID:: Restore files to given BLOB_ID
33 # Restore the file to the state appropriate to the given ID.
34 # The list of files to recover is mandatory in this case.
36 # Testsuite: Marginal (part of t9202-merge-on-dirty)
38 USAGE
="cg-restore [-f] [-r ID] [FILE]..."
40 .
"${COGITO_LIB}"cg-Xlib ||
exit 1
47 elif optparse
-r=; then
48 objid
="$(cg-object-id -n "${OPTARG}")" ||
exit 1
58 objtype
="$(git-cat-file -t "$objid")"
59 if [ "$objtype" = "commit" ]; then
60 objid
="$(cg-object-id -t "$objid")"
64 objid
="$(cg-object-id -t)"
69 if [ -n "$force" ]; then
70 for file in "${ARGS[@]}"; do
71 files
[${#files[@]}]="${_git_relpath}$file"
74 # Not forcing, filter out existing files
75 for file in "${ARGS[@]}"; do
76 if [ -e "${_git_relpath}$file" ]; then
77 echo "Error: File $file already exists; use -f to override" >&2
81 files
[${#files[@]}]="${_git_relpath}$file"
83 [ ${#files[@]} -ge 1 ] || die
"no files suitable for restoring left"
86 if [ "$objtype" = "tree" ]; then
87 TMPFILE
="$(mktemp -t gitrestore.XXXXXX)" ||
exit 1
88 if ! git-ls-tree
-r "$objid" "${files[@]}" |
89 sed -ne 's/^\([0-7]*\) blob \(.*\)$/\1 \2/p' |
90 ( ret
=0; while read mode id name
; do
91 echo "Restoring file ${name#$_git_relpath}"
92 # TODO: Use git-update-index --index-info when we
93 # will depend on git new enough. --pasky
94 if ! git-update-index
--add --cacheinfo "$mode" "$id" "$name"; then
95 echo "Error: Cannot mark ${name#$_git_relpath} for update" >&2
99 echo "$name" >>$TMPFILE
101 done; exit $ret ); then
103 # When we'll do --index-info which should be atomic:
105 # echo "Fatal: git-update-index failed, cancelling the whole operation (restored nothing)" >&2
109 cat "$TMPFILE" |
tr '\n' '\0' |
xargs -0 git-checkout-index
-u $force -- || ret
=1
112 elif [ "$objtype" = "blob" ]; then
113 [ "${#files[@]}" -gt 1 ] && warn
"restoring multiple files to a single blob"
114 for file in "${files[@]}"; do
115 echo "Restoring file ${file#$_git_relpath}"
116 warn
"file ${file#$_git_relpath} likely will not have correct permissions"
117 git-cat-file blob
"$objid" >"$file"
118 git-update-index
-- "$file" || ret
=1
122 else # no arguments - much weaker
123 [ "$objid" ] && die
"you need to pass an explicit list of files to -r"
124 [ "$_git_relpath" ] && die
"cannot restore files en masse in subdirectories yet"
125 if [ "$(git-ls-files --deleted)" ]; then
126 git-ls-files
--deleted |
sed "s/^/Restoring file /"
128 git-checkout-index
-u -q -a $force