'uci - how to revert all unstaged changes

uci documentation says:

All "uci set", "uci add", "uci rename" and "uci delete" commands are staged into a temporary location and written to flash at once with "uci commit".

If I get it right, you first run some commands like the ones mentioned above and to make the changes be written to the configuration files you run uci commit. For example, let's say I have done the following changes...

root@OpenWrt:~# uci changes
network.vlan15.ifname='eth1.15'
network.vlan15.type='bridge'
network.vlan15.proto='static'
network.vlan15.netmask='255.255.255.0'
network.vlan15.ipaddr='192.168.10.0'

...but I don't want to continue and commit them. Is there an easy way to revert all staged changes and avoid doing it one by one?



Solution 1:[1]

There is a command to revert all staged changes

revert  <config>[.<section>[.<option>]]     Revert the given option, section or configuration file.

So, in your case, it should be

uci revert network.vlan15

See https://openwrt.org/docs/guide-user/base-system/uci

Solution 2:[2]

This should be possible by the following command:

root@firlefanz:~# rm -rf /tmp/.uci/

Solution 3:[3]

This one-liner should do the trick:

uci changes | sed -rn 's%^[+-]?([^=+-]*)([+-]?=.*|)$%\1%' | xargs -n 1 uci revert

tl;dr The sed command extracts the option names from the staged changes. The xargs command executes the revert command for every extracted option.

Now let's have a deep dive into everything:

uci changes prints the prepared changes which are then piped to the sed command.

The sed opton -r enables extended regular expressions and -n suppress automatic printing of pattern matches.

The sed command s is used to do a search and replace and % is used as separation character for the search and replace term.

The uci change lines have different formats.

Removed configuration options are prefixed with -. Added configuration options are prefixed with + Changed options don't have a prefix.

To match the prefixes [+-]? is used. A question mark means, that one of the characters in the square brackets can be matched optional.

The option name will be matched with the pattern [^=+-]*. This regex has the meaning of any number of characters as long as the character is not one of =+-. It is inside round brackets to mark it as group to reuse it later.

The next pattern ([+-]?=.*|) is also a pattern group. There are two different groups spitted by the pipe. The second part is the easy one and means no character at all. This happens when a uci option is deleted. The fist part means that the character = can optional prepended with + or -. After the = can be one or more characters which is indicated by .*. =<value> happens on added configuration. The prepending of - or + indicates the value is removed from the list or added to the list if the option is a list.

In the replace pattern the whole line is replaced with the first group by its reference \1. In other words: only the option name is printed.

All the option names are then send to xargs. With option -n 1 xargs execuse uci revert <option_name> for every option_name send by sed.

This are some examples for the different formats of the uci changes output:

-a
+b='create new option with this value'
c='change an existing option to this value'
d+='appended to list'
e-='removed from list'

The extraced option names will be the following:

a
b
c
d
e

xargs -n 1 will then executed the following commands:

uci revert a
uci revert b
uci revert c
uci revert d
uci revert e

This is the whole magic of the one-liner.

Solution 4:[4]

I didn't find a uci command to revert all uncommitted changes, but you can probably parse the output of the uci changes command with some shell scripting to achieve the desired result. Here is an example script:

#!/bin/ash

# uci-revert-all.sh
# Revert all uncommitted uci changes

# Iterate over changed settings
# Each line has the form of an equation, e.g. parameter=value
for setting in $(uci changes); do

    # Extract parameter from equation
    parameter=$(echo ${setting} | grep -o '^\(\w\|[._-]\)\+')

    # Display a status message
    echo "Reverting: ${parameter}"

    # Revert the setting for the given parameter
    uci revert "${parameter}"
done

A simpler alternative might be to use the uci revert <config> syntax, e.g.:

#!/bin/ash

# uci-revert-all.sh
# Revert all uncommitted uci changes

for config in /etc/config/*; do
    uci revert $(basename ${config})
done

Both of these approaches worked well for me on a router running LEDE 4.

Solution 5:[5]

Here's another short one-liner to revert ALL unstaged changes (as per the question):

for i in /etc/config/* ; do uci revert ${i##*/} ; done

(FYI, this uses posix parameter expansion's "Remove Largest Prefix Pattern".)

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Clèm
Solution 2 Mischa Diehm
Solution 3
Solution 4
Solution 5 jaimet