Add new UI elements for showing the (change of) file mode
Files in Git can only have 4 different modes. Add an enum for that.
The backend has recently added `new_mode` and `old_mode` properties
to the `FileInfo` entity, so let's make use of that and show this
information to the user.
For regular files nothing will change. We don't show any information
when adding, deleting or modifying regular files.
But when the file mode changes we show a warning icon. And if the new
file is not regular, then we show the file mode after the file name.
We also show a tooltip with details about the file mode.
In the diff view we expand the octal file mode to something readable.
For testing we have created a dedicated change with various file modes
and file mode changes:
https://testing-review.googlesource.com/c/file-modes/+/1501
Screenshot https://imgur.com/a/r8zC8kq
Release-Notes: skip
Google-Bug-Id: b/251087598
Change-Id: I79f4a5dbe7613900a4799930a2f6369e398d96ba
diff --git a/polygerrit-ui/app/utils/file-util.ts b/polygerrit-ui/app/utils/file-util.ts
new file mode 100644
index 0000000..246ac20
--- /dev/null
+++ b/polygerrit-ui/app/utils/file-util.ts
@@ -0,0 +1,45 @@
+/**
+ * @license
+ * Copyright 2022 Google LLC
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+/** See also Patch.java for the backend equivalent. */
+export enum FileMode {
+ /** Mode indicating an entry is a symbolic link. */
+ SYMLINK = 0o120000,
+
+ /** Mode indicating an entry is a non-executable file. */
+ REGULAR_FILE = 0o100644,
+
+ /** Mode indicating an entry is an executable file. */
+ EXECUTABLE_FILE = 0o100755,
+
+ /** Mode indicating an entry is a submodule commit in another repository. */
+ GITLINK = 0o160000,
+}
+
+export function fileModeToString(mode?: number, includeNumber = true): string {
+ const str = fileModeStr(mode);
+ const num = mode?.toString(8);
+ return `${str}${includeNumber && str ? ` (${num})` : ''}`;
+}
+
+function fileModeStr(mode?: number): string {
+ if (mode === FileMode.SYMLINK) return 'symlink';
+ if (mode === FileMode.REGULAR_FILE) return 'regular';
+ if (mode === FileMode.EXECUTABLE_FILE) return 'executable';
+ if (mode === FileMode.GITLINK) return 'gitlink';
+ return '';
+}
+
+export function expandFileMode(input?: string) {
+ if (!input) return input;
+ for (const modeNum of Object.values(FileMode) as FileMode[]) {
+ const modeStr = modeNum?.toString(8);
+ if (input.includes(modeStr)) {
+ return input.replace(modeStr, `${fileModeToString(modeNum)}`);
+ }
+ }
+ return input;
+}