28 Commits

Author SHA1 Message Date
9c63ea625b v2.4.0 2015-02-23 02:05:18 +01:00
7258e29656 Remove typo 2015-02-23 01:27:25 +01:00
781b5104e4 Add travis badge to the readme 2015-02-23 01:21:13 +01:00
f4f36a5af9 Update changelog 2015-02-23 01:19:19 +01:00
d4ec690610 Use more 'tmux-test' functions 2015-02-23 01:19:05 +01:00
d9ac38bef9 Update 'tmux-test' 2015-02-23 01:18:45 +01:00
c5bc35932b Run setup task from .travis.yml 2015-02-23 00:46:41 +01:00
a3dd99085a Fix tests for travis 2015-02-23 00:05:54 +01:00
157bd5e38a Do not gitignore 'tmux-test' files 2015-02-22 22:31:57 +01:00
890a413b9f Update changelog 2015-02-22 21:54:20 +01:00
9cabda7c71 Resurrect restore test and updates 2015-02-22 21:53:33 +01:00
66916085d9 Update resurrect save test 2015-02-22 21:20:46 +01:00
30366a3cb4 Test resurrect save feature 2015-02-22 20:49:59 +01:00
ac86e025ad Setup 'tmux-test' 2015-02-22 18:41:58 +01:00
8b183f373f Readme update 2015-02-20 14:18:24 +01:00
5c1105c579 Update readme 2015-02-20 14:16:46 +01:00
3dd5441d42 Link to tmux-continuum instead of tmux-resurrect-auto 2015-02-20 13:44:46 +01:00
703df121e2 Readme update 2015-02-12 15:31:32 +01:00
2bc9bc0dd7 Mention tmux-resurrect-auto in the readme 2015-02-12 14:21:16 +01:00
671699a054 v2.3.0 2015-02-12 14:13:22 +01:00
b7e7669999 Improve fetching "window_layout" value
It's faster now.
2015-02-12 14:10:56 +01:00
952e1f9784 v2.2.0 2015-02-12 12:53:47 +01:00
abad85f03b Enable quiet saving 2015-02-12 12:53:19 +01:00
e1b01ee4f9 Export script paths so that 'tmux-resurrect-auto' plugin can use them 2015-02-12 12:48:44 +01:00
708cd49d31 Fix a zoomed windows related regression 2015-02-12 12:47:09 +01:00
47db8198d3 v2.1.0 2015-02-12 03:45:15 +01:00
c4375bf642 Enable 'full restore' by overwriting a single pane 2015-02-10 15:16:26 +01:00
ddf9c5ef87 Reorder function in restore script 2015-02-10 13:44:14 +01:00
22 changed files with 406 additions and 56 deletions

3
.gitignore vendored Normal file
View File

@ -0,0 +1,3 @@
run_tests
tests/run_tests_in_isolation
tests/helpers/helpers.sh

3
.gitmodules vendored Normal file
View File

@ -0,0 +1,3 @@
[submodule "lib/tmux-test"]
path = lib/tmux-test
url = https://github.com/tmux-plugins/tmux-test.git

14
.travis.yml Normal file
View File

@ -0,0 +1,14 @@
# generic packages and latest Tmux 1.9a
before_install:
- sudo apt-get update
- sudo apt-get install -y git-core expect
- sudo apt-get install -y python-software-properties software-properties-common
- sudo add-apt-repository -y ppa:pi-rho/dev
- sudo apt-get update
- sudo apt-get install -y tmux=1.9a-1~ppa1~p
install:
- git fetch --unshallow --recurse-submodules || git fetch --recurse-submodules
- lib/tmux-test/setup
script: ./tests/run_tests_in_isolation

View File

@ -2,6 +2,28 @@
### master
### v2.4.0, 2015-02-23
- add "tmux-test"
- add test for "resurrect save" feature
- add test for "resurrect restore" feature
- make the tests work and pass on travis
- add travis badge to the readme
### v2.3.0, 2015-02-12
- Improve fetching proper window_layout for zoomed windows. In order to fetch
proper value, window has to get unzoomed. This is now done faster so that
"unzoom,fetch value,zoom" cycle is almost unnoticable to the user.
### v2.2.0, 2015-02-12
- bugfix: zoomed windows related regression
- export save and restore script paths so that 'tmux-resurrect-save' plugin can
use them
- enable "quiet" saving (used by 'tmux-resurrect-save' plugin)
### v2.1.0, 2015-02-12
- if restore is started when there's only **1 pane in the whole tmux server**,
assume the users wants the "full restore" and overrwrite that pane.
### v2.0.0, 2015-02-10
- add link to the wiki page for "first pane/window issue" to the README as well
as other tweaks

View File

@ -1,5 +1,7 @@
# Tmux Resurrect
[![Build Status](https://travis-ci.org/tmux-plugins/tmux-resurrect.png?branch=master)](https://travis-ci.org/tmux-plugins/tmux-resurrect)
Restore `tmux` environment after a system restart.
Tmux is great, except when you have to restart the computer. You lose all the
@ -14,6 +16,9 @@ No configuration is required. You should feel like you never quit tmux.
It even (optionally) [restores vim and neovim sessions](#restoring-vim-and-neovim-sessions)!
Automatic restoring and continuous saving of tmux env is also possible with
[tmux-continuum](https://github.com/tmux-plugins/tmux-continuum) plugin.
### Screencast
[![screencast screenshot](/video/screencast_img.png)](https://vimeo.com/104763018)
@ -51,15 +56,10 @@ This plugin goes to great lengths to save and restore all the details from your
Requirements / dependencies: `tmux 1.9` or higher, `bash`.
`tmux-resurrect` is idempotent! It will not try to restore panes or windows that
already exist.
### FAQ
> I have a problem: first pane/window is not restoring!
Check out
[this wiki page](https://github.com/tmux-plugins/tmux-resurrect/wiki/Help:-issues-with-the-first-window)
for the explanation and problem solution.
already exist.<br/>
The single exception to this is when tmux is started with only 1 pane in order
to restore previous tmux env. In this case only will this single pane be
overwritten.
### Installation with [Tmux Plugin Manager](https://github.com/tmux-plugins/tpm) (recommended)
@ -164,6 +164,11 @@ add `HISTCONTROL=ignoreboth` to your `.bashrc` (this is set by default in Ubuntu
highlighted text to system clipboard
- [tmux-open](https://github.com/tmux-plugins/tmux-open) - a plugin for quickly
opening highlighted file or a url
- [tmux-continuum](https://github.com/tmux-plugins/tmux-continuum) - automatic
restoring and continuous saving of tmux env
You might want to follow [@brunosutic](https://twitter.com/brunosutic) on
twitter if you want to hear about new tmux plugins or feature updates.
### Reporting bugs and contributing

1
lib/tmux-test Submodule

Submodule lib/tmux-test added at 1778f06b20

View File

@ -25,9 +25,15 @@ set_default_strategies() {
tmux set-option -g "${restore_process_strategy_option}irb" "default_strategy"
}
set_script_path_options() {
tmux set-option -g "$save_path_option" "$CURRENT_DIR/scripts/save.sh"
tmux set-option -g "$restore_path_option" "$CURRENT_DIR/scripts/restore.sh"
}
main() {
set_save_bindings
set_restore_bindings
set_default_strategies
set_script_path_options
}
main

1
run_tests Symbolic link
View File

@ -0,0 +1 @@
lib/tmux-test/run_tests

View File

@ -85,10 +85,3 @@ resurrect_history_file() {
local pane_id="$1"
echo "$(resurrect_dir)/bash_history-${pane_id}"
}
restore_zoomed_windows() {
awk 'BEGIN { FS="\t"; OFS="\t" } /^pane/ && $6 ~ /Z/ && $9 == 1 { print $2, $3; }' $(last_resurrect_file) |
while IFS=$d read session_name window_number; do
tmux resize-pane -t "${session_name}:${window_number}" -Z
done
}

View File

@ -16,6 +16,8 @@ d=$'\t'
# is also not restored. That makes the restoration process more idempotent.
EXISTING_PANES_VAR=""
RESTORING_FROM_SCRATCH="false"
is_line_type() {
local line_type="$1"
local line="$2"
@ -56,6 +58,14 @@ is_pane_registered_as_existing() {
[[ "$EXISTING_PANES_VAR" =~ "$pane_custom_id" ]]
}
restore_from_scratch_true() {
RESTORING_FROM_SCRATCH="true"
}
is_restoring_from_scratch() {
[ "$RESTORING_FROM_SCRATCH" == "true" ]
}
window_exists() {
local session_name="$1"
local window_number="$2"
@ -114,9 +124,17 @@ restore_pane() {
window_name="$(remove_first_char "$window_name")"
pane_full_command="$(remove_first_char "$pane_full_command")"
if pane_exists "$session_name" "$window_number" "$pane_index"; then
# Pane exists, no need to create it!
# Pane existence is registered. Later, it's process also isn't restored.
register_existing_pane "$session_name" "$window_number" "$pane_index"
if is_restoring_from_scratch; then
# overwrite the pane
# happens only for the first pane if it's the only registered pane for the whole tmux server
local pane_id="$(tmux display-message -p -F "#{pane_id}" -t "$session_name:$window_number")"
new_pane "$session_name" "$window_number" "$window_name" "$dir"
tmux kill-pane -t "$pane_id"
else
# Pane exists, no need to create it!
# Pane existence is registered. Later, its process also won't be restored.
register_existing_pane "$session_name" "$window_number" "$pane_index"
fi
elif window_exists "$session_name" "$window_number"; then
new_pane "$session_name" "$window_number" "$window_name" "$dir"
elif session_exists "$session_name"; then
@ -136,7 +154,48 @@ restore_state() {
done
}
restore_grouped_session() {
local grouped_session="$1"
echo "$grouped_session" |
while IFS=$d read line_type grouped_session original_session alternate_window active_window; do
TMUX="" tmux -S "$(tmux_socket)" new-session -d -s "$grouped_session" -t "$original_session"
done
}
restore_active_and_alternate_windows_for_grouped_sessions() {
local grouped_session="$1"
echo "$grouped_session" |
while IFS=$d read line_type grouped_session original_session alternate_window_index active_window_index; do
alternate_window_index="$(remove_first_char "$alternate_window_index")"
active_window_index="$(remove_first_char "$active_window_index")"
if [ -n "$alternate_window_index" ]; then
tmux switch-client -t "${grouped_session}:${alternate_window_index}"
fi
if [ -n "$active_window_index" ]; then
tmux switch-client -t "${grouped_session}:${active_window_index}"
fi
done
}
never_ever_overwrite() {
local overwrite_option_value="$(get_tmux_option "$overwrite_option" "")"
[ -n "$overwrite_option_value" ]
}
detect_if_restoring_from_scratch() {
if never_ever_overwrite; then
return
fi
local total_number_of_panes="$(tmux list-panes -a | wc -l | sed 's/ //g')"
if [ "$total_number_of_panes" -eq 1 ]; then
restore_from_scratch_true
fi
}
# functions called from main (ordered)
restore_all_panes() {
detect_if_restoring_from_scratch
while read line; do
if is_line_type "pane" "$line"; then
restore_pane "$line"
@ -144,6 +203,13 @@ restore_all_panes() {
done < $(last_resurrect_file)
}
restore_pane_layout_for_each_window() {
\grep '^window' $(last_resurrect_file) |
while IFS=$d read line_type session_name window_number window_active window_flags window_layout; do
tmux select-layout -t "${session_name}:${window_number}" "$window_layout"
done
}
restore_shell_history() {
awk 'BEGIN { FS="\t"; OFS="\t" } /^pane/ { print $2, $3, $7, $10; }' $(last_resurrect_file) |
while IFS=$d read session_name window_number pane_index pane_command; do
@ -171,13 +237,6 @@ restore_all_pane_processes() {
fi
}
restore_pane_layout_for_each_window() {
\grep '^window' $(last_resurrect_file) |
while IFS=$d read line_type session_name window_number window_active window_flags window_layout; do
tmux select-layout -t "${session_name}:${window_number}" "$window_layout"
done
}
restore_active_pane_for_each_window() {
awk 'BEGIN { FS="\t"; OFS="\t" } /^pane/ && $9 == 1 { print $2, $3, $7; }' $(last_resurrect_file) |
while IFS=$d read session_name window_number active_pane; do
@ -186,27 +245,11 @@ restore_active_pane_for_each_window() {
done
}
restore_grouped_session() {
local grouped_session="$1"
echo "$grouped_session" |
while IFS=$d read line_type grouped_session original_session alternate_window active_window; do
TMUX="" tmux -S "$(tmux_socket)" new-session -d -s "$grouped_session" -t "$original_session"
done
}
restore_active_and_alternate_windows_for_grouped_sessions() {
local grouped_session="$1"
echo "$grouped_session" |
while IFS=$d read line_type grouped_session original_session alternate_window_index active_window_index; do
alternate_window_index="$(remove_first_char "$alternate_window_index")"
active_window_index="$(remove_first_char "$active_window_index")"
if [ -n "$alternate_window_index" ]; then
tmux switch-client -t "${grouped_session}:${alternate_window_index}"
fi
if [ -n "$active_window_index" ]; then
tmux switch-client -t "${grouped_session}:${active_window_index}"
fi
done
restore_zoomed_windows() {
awk 'BEGIN { FS="\t"; OFS="\t" } /^pane/ && $6 ~ /Z/ && $9 == 1 { print $2, $3; }' $(last_resurrect_file) |
while IFS=$d read session_name window_number; do
tmux resize-pane -t "${session_name}:${window_number}" -Z
done
}
restore_grouped_sessions() {

View File

@ -10,6 +10,9 @@ source "$CURRENT_DIR/spinner_helpers.sh"
d=$'\t'
delimiter=$'\t'
# if "quiet" script produces no output
SCRIPT_OUTPUT="$1"
grouped_sessions_format() {
local format
format+="#{session_grouped}"
@ -82,6 +85,11 @@ dump_windows_raw(){
tmux list-windows -a -F "$(window_format)"
}
toggle_window_zoom() {
local target="$1"
tmux resize-pane -Z -t "$target"
}
_save_command_strategy_file() {
local save_command_strategy="$(get_tmux_option "$save_command_strategy_option" "$default_save_command_strategy")"
local strategy_file="$CURRENT_DIR/../save_command_strategies/${save_command_strategy}.sh"
@ -164,11 +172,6 @@ dump_panes() {
if is_session_grouped "$session_name"; then
continue
fi
# check if current pane is part of a maximized window and if the pane is active
if [[ "${window_flags}" == *Z* ]] && [[ ${pane_active} == 1 ]]; then
# unmaximize the pane
tmux resize-pane -Z -t "${session_name}:${window_number}"
fi
full_command="$(pane_full_command $pane_pid)"
echo "${line_type}${d}${session_name}${d}${window_number}${d}${window_name}${d}${window_active}${d}${window_flags}${d}${pane_index}${d}${dir}${d}${pane_active}${d}${pane_command}${d}:${full_command}"
done
@ -181,6 +184,15 @@ dump_windows() {
if is_session_grouped "$session_name"; then
continue
fi
# window_layout is not correct for zoomed windows
if [[ "$window_flags" == *Z* ]]; then
# unmaximize the window
toggle_window_zoom "${session_name}:${window_index}"
# get correct window layout
window_layout="$(tmux display-message -p -t "${session_name}:${window_index}" -F "#{window_layout}")"
# maximize window again
toggle_window_zoom "${session_name}:${window_index}"
fi
echo "${line_type}${d}${session_name}${d}${window_index}${d}${window_active}${d}${window_flags}${d}${window_layout}"
done
}
@ -207,15 +219,22 @@ save_all() {
if save_bash_history_option_on; then
dump_bash_history
fi
restore_zoomed_windows
}
show_output() {
[ "$SCRIPT_OUTPUT" != "quiet" ]
}
main() {
if supported_tmux_version_ok; then
start_spinner "Saving..." "Tmux environment saved!"
if show_output; then
start_spinner "Saving..." "Tmux environment saved!"
fi
save_all
stop_spinner
display_message "Tmux environment saved!"
if show_output; then
stop_spinner
display_message "Tmux environment saved!"
fi
fi
}
main

View File

@ -1,9 +1,11 @@
# key bindings
default_save_key="C-s"
save_option="@resurrect-save"
save_path_option="@resurrect-save-script-path"
default_restore_key="C-r"
restore_option="@resurrect-restore"
restore_path_option="@resurrect-restore-script-path"
# default processes that are restored
default_proc_list_option="@resurrect-default-processes"
@ -28,3 +30,6 @@ save_command_strategy_option="@resurrect-save-command-strategy"
default_save_command_strategy="ps"
bash_history_option="@resurrect-save-bash-history"
# set to 'on' to ensure panes are never ever overwritten
overwrite_option="@resurrect-never-overwrite"

21
tests/fixtures/restore_file.txt vendored Normal file
View File

@ -0,0 +1,21 @@
pane 0 0 :bash 1 :* 0 :/tmp 1 bash :
pane blue 0 :vim 0 : 0 :/tmp 1 vim :vim foo.txt
pane blue 1 :man 0 :- 0 :/tmp 0 bash :
pane blue 1 :man 0 :- 1 :/usr/share/man 1 man :man echo
pane blue 2 :bash 1 :* 0 :/tmp 1 bash :
pane red 0 :bash 0 : 0 :/tmp 1 bash :
pane red 1 :bash 0 :- 0 :/tmp 0 bash :
pane red 1 :bash 0 :- 1 :/tmp 0 bash :
pane red 1 :bash 0 :- 2 :/tmp 1 bash :
pane red 2 :bash 1 :* 0 :/tmp 0 bash :
pane red 2 :bash 1 :* 1 :/tmp 1 bash :
pane yellow 0 :bash 1 :* 0 :/tmp/bar 1 bash :
window 0 0 1 :* ce9e,200x49,0,0,1
window blue 0 0 : ce9f,200x49,0,0,2
window blue 1 0 :- 178b,200x49,0,0{100x49,0,0,3,99x49,101,0,4}
window blue 2 1 :* cea2,200x49,0,0,5
window red 0 0 : cea3,200x49,0,0,6
window red 1 0 :- 135b,200x49,0,0[200x24,0,0,7,200x24,0,25{100x24,0,25,8,99x24,101,25,9}]
window red 2 1 :* db81,200x49,0,0[200x24,0,0,10,200x24,0,25,11]
window yellow 0 1 :* 6781,200x49,0,0,12
state yellow blue

21
tests/fixtures/save_file.txt vendored Normal file
View File

@ -0,0 +1,21 @@
pane 0 0 :bash 1 :* 0 :/tmp 1 bash :
pane blue 0 :vim 0 : 0 :/tmp 1 vim :vim foo.txt
pane blue 1 :man 0 :- 0 :/tmp 0 bash :
pane blue 1 :man 0 :- 1 :/usr/share/man 1 man :man echo
pane blue 2 :bash 1 :* 0 :/tmp 1 bash :
pane red 0 :bash 0 : 0 :/tmp 1 bash :
pane red 1 :bash 0 :- 0 :/tmp 0 bash :
pane red 1 :bash 0 :- 1 :/tmp 0 bash :
pane red 1 :bash 0 :- 2 :/tmp 1 bash :
pane red 2 :bash 1 :* 0 :/tmp 0 bash :
pane red 2 :bash 1 :* 1 :/tmp 1 bash :
pane yellow 0 :bash 1 :* 0 :/tmp/bar 1 bash :
window 0 0 1 :* ce9d,200x49,0,0,0
window blue 0 0 : cea4,200x49,0,0,7
window blue 1 0 :- 9797,200x49,0,0{100x49,0,0,8,99x49,101,0,9}
window blue 2 1 :* 677f,200x49,0,0,10
window red 0 0 : ce9e,200x49,0,0,1
window red 1 0 :- 52b7,200x49,0,0[200x24,0,0,2,200x24,0,25{100x24,0,25,3,99x24,101,25,4}]
window red 2 1 :* bd68,200x49,0,0[200x24,0,0,5,200x24,0,25,6]
window yellow 0 1 :* 6780,200x49,0,0,11
state yellow blue

View File

@ -0,0 +1,41 @@
#!/usr/bin/env expect
source "./tests/helpers/expect_helpers.exp"
expect_setup
spawn tmux
# delay with sleep to compensate for tmux starting time
sleep 1
run_shell_command "cd /tmp"
# session red
new_tmux_session "red"
new_tmux_window
horizontal_split
vertical_split
new_tmux_window
horizontal_split
# session blue
new_tmux_session "blue"
run_shell_command "touch foo.txt"
run_shell_command "vim foo.txt"
new_tmux_window
vertical_split
run_shell_command "man echo"
new_tmux_window
# session yellow
new_tmux_session "yellow"
run_shell_command "cd /tmp/bar"
start_resurrect_save
run_shell_command "tmux kill-server"

View File

@ -0,0 +1,65 @@
# a set of expect helpers
# basic setup for each script
proc expect_setup {} {
# disables script output
log_user 0
# standard timeout
set timeout 5
}
proc new_tmux_window {} {
send "c"
send "cd /tmp\r"
sleep 0.2
}
proc rename_current_session {name} {
send "$"
# delete existing name with ctrl-u
send ""
send "$name\r"
sleep 0.2
}
proc new_tmux_session {name} {
send "TMUX='' tmux new -d -s $name\r"
sleep 1
send "tmux switch-client -t $name\r"
send "cd /tmp\r"
sleep 0.5
}
proc horizontal_split {} {
send "\""
sleep 0.2
send "cd /tmp\r"
sleep 0.1
}
proc vertical_split {} {
send "%"
sleep 0.2
send "cd /tmp\r"
sleep 0.1
}
proc run_shell_command {command} {
send "$command\r"
sleep 1
}
proc start_resurrect_save {} {
send ""
sleep 5
}
proc start_resurrect_restore {} {
send ""
sleep 10
}
proc clear_screen_for_window {target} {
send "tmux send-keys -t $target C-l\r"
sleep 0.2
}

1
tests/helpers/helpers.sh Symbolic link
View File

@ -0,0 +1 @@
../../lib/tmux-test/tests/helpers/helpers.sh

View File

@ -0,0 +1,18 @@
#!/usr/bin/env expect
source "./tests/helpers/expect_helpers.exp"
expect_setup
spawn tmux
# delay with sleep to compensate for tmux starting time
sleep 1
start_resurrect_restore
# delete all existing resurrect save files
run_shell_command "rm ~/.tmux/resurrect/*"
start_resurrect_save
run_shell_command "tmux kill-server"

View File

@ -0,0 +1,11 @@
# we want "fixed" dimensions no matter the size of real display
set_screen_dimensions_helper() {
stty cols 200
stty rows 50
}
last_save_file_differs_helper() {
local original_file="$1"
diff "$original_file" "${HOME}/.tmux/resurrect/last"
[ $? -ne 0 ]
}

View File

@ -0,0 +1 @@
../lib/tmux-test/tests/run_tests_in_isolation

33
tests/test_resurrect_restore.sh Executable file
View File

@ -0,0 +1,33 @@
#!/usr/bin/env bash
CURRENT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
source $CURRENT_DIR/helpers/helpers.sh
source $CURRENT_DIR/helpers/resurrect_helpers.sh
setup_before_restore() {
# setup restore file
mkdir -p ~/.tmux/resurrect/
cp tests/fixtures/restore_file.txt "${HOME}/.tmux/resurrect/restore_file.txt"
ln -sf restore_file.txt "${HOME}/.tmux/resurrect/last"
# directory used in restored tmux session
mkdir -p /tmp/bar
}
restore_tmux_environment_and_save_again() {
set_screen_dimensions_helper
$CURRENT_DIR/helpers/restore_and_save_tmux_test_environment.exp
}
main() {
install_tmux_plugin_under_test_helper
setup_before_restore
restore_tmux_environment_and_save_again
if last_save_file_differs_helper "tests/fixtures/restore_file.txt"; then
fail_helper "Saved file not correct after restore"
fi
exit_helper
}
main

23
tests/test_resurrect_save.sh Executable file
View File

@ -0,0 +1,23 @@
#!/usr/bin/env bash
CURRENT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
source $CURRENT_DIR/helpers/helpers.sh
source $CURRENT_DIR/helpers/resurrect_helpers.sh
create_tmux_test_environment_and_save() {
set_screen_dimensions_helper
$CURRENT_DIR/helpers/create_and_save_tmux_test_environment.exp
}
main() {
install_tmux_plugin_under_test_helper
mkdir -p /tmp/bar # setup required dirs
create_tmux_test_environment_and_save
if last_save_file_differs_helper "tests/fixtures/save_file.txt"; then
fail_helper "Saved file not correct (initial save)"
fi
exit_helper
}
main