Skip to content

Commit 8629743

Browse files
committed
添加文件
1 parent 52b1e01 commit 8629743

80 files changed

Lines changed: 6037 additions & 0 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/workflows/check.yml

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
name: Flutter CI
2+
3+
on: [ push, pull_request ]
4+
5+
jobs:
6+
lint-test-build:
7+
name: Lint, Test, and Build
8+
runs-on: windows-latest
9+
10+
steps:
11+
12+
- name: Checkout repository
13+
uses: actions/checkout@v4
14+
15+
16+
- name: Set up Flutter
17+
uses: subosito/flutter-action@v2
18+
with:
19+
cache: true
20+
channel: stable
21+
flutter-version-file: pubspec.yaml
22+
23+
- name: Install dependencies
24+
run: flutter pub get
25+
26+
- name: Analyze project source
27+
run: flutter analyze
28+
29+
- name: Run tests
30+
run: flutter test
31+
32+
- name: Build Windows release
33+
run: flutter build windows --release
34+
35+
- name: Upload artifact
36+
uses: actions/upload-artifact@v4
37+
with:
38+
name: windows-release
39+
path: build/windows/x64/runner/Release
40+
overwrite: true
41+
compression-level: 9
42+
retention-days: 90

.gitignore

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
# Miscellaneous
2+
*.class
3+
*.log
4+
*.pyc
5+
*.swp
6+
.DS_Store
7+
.atom/
8+
.build/
9+
.buildlog/
10+
.history
11+
.svn/
12+
.swiftpm/
13+
migrate_working_dir/
14+
15+
# IntelliJ related
16+
*.iml
17+
*.ipr
18+
*.iws
19+
.idea/
20+
21+
# The .vscode folder contains launch configuration and tasks you configure in
22+
# VS Code which you may wish to be included in version control, so this line
23+
# is commented out by default.
24+
#.vscode/
25+
26+
# Flutter/Dart/Pub related
27+
**/doc/api/
28+
**/ios/Flutter/.last_build_id
29+
.dart_tool/
30+
.flutter-plugins-dependencies
31+
.pub-cache/
32+
.pub/
33+
/build/
34+
/coverage/
35+
36+
# Symbolication related
37+
app.*.symbols
38+
39+
# Obfuscation related
40+
app.*.map.json
41+
42+
# Android Studio will place build artifacts here
43+
/android/app/debug
44+
/android/app/profile
45+
/android/app/release

.metadata

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
# This file tracks properties of this Flutter project.
2+
# Used by Flutter tool to assess capabilities and perform upgrades etc.
3+
#
4+
# This file should be version controlled and should not be manually edited.
5+
6+
version:
7+
revision: "20f82749394e68bcfbbeee96bad384abaae09c13"
8+
channel: "stable"
9+
10+
project_type: app
11+
12+
# Tracks metadata for the flutter migrate command
13+
migration:
14+
platforms:
15+
- platform: root
16+
create_revision: 20f82749394e68bcfbbeee96bad384abaae09c13
17+
base_revision: 20f82749394e68bcfbbeee96bad384abaae09c13
18+
- platform: linux
19+
create_revision: 20f82749394e68bcfbbeee96bad384abaae09c13
20+
base_revision: 20f82749394e68bcfbbeee96bad384abaae09c13
21+
- platform: macos
22+
create_revision: 20f82749394e68bcfbbeee96bad384abaae09c13
23+
base_revision: 20f82749394e68bcfbbeee96bad384abaae09c13
24+
- platform: windows
25+
create_revision: 20f82749394e68bcfbbeee96bad384abaae09c13
26+
base_revision: 20f82749394e68bcfbbeee96bad384abaae09c13
27+
28+
# User provided section
29+
30+
# List of Local paths (relative to this file) that should be
31+
# ignored by the migrate tool.
32+
#
33+
# Files that are not part of the templates will be ignored by default.
34+
unmanaged_files:
35+
- 'lib/main.dart'
36+
- 'ios/Runner.xcodeproj/project.pbxproj'

README.md

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
# Git 账号切换器 (Git Switcher)
2+
3+
一款使用 Flutter 开发的跨平台 Git 账号和 SSH 配置快速切换工具,可以在多个 Git 账号(如个人账号、工作账号)之间高效、安全地进行切换。
4+
5+
## 核心功能
6+
7+
* **多配置管理**:轻松新建、修改、删除和查看多个 Git 账号配置。
8+
* **一键快速切换**:在不同的 Git 和 SSH 配置之间实现一键切换,无需手动编辑文件。
9+
* **自动备份与恢复**
10+
* 每次切换配置时,自动备份当前的 `.gitconfig``.ssh/config` 文件。
11+
* 提供备份列表,可随时恢复到任一历史版本。
12+
* 可自定义是否启用备份及最大备份数量。
13+
* **配置状态校验**
14+
* 自动检测当前系统的 Git/SSH 配置与哪个预设的配置相匹配。
15+
* 切换前检查 SSH 配置冲突,并提供确认提示,防止误操作。
16+
* 校验 SSH 私钥文件的存在性和权限(在 Linux/macOS 下应为 600),确保配置的有效性。
17+
* **跨平台支持**:兼容 Windows、macOS 和 Linux 操作系统
18+
19+
## 如何使用
20+
21+
1. **启动应用**:打开应用后,您会看到主界面。
22+
2. **创建配置**
23+
* 点击右下角的“+”按钮,进入新建配置页面。
24+
* **配置名称**:为您的配置起一个易于识别的名称,如“工作账号”。
25+
* **Git 配置内容**:可以直接粘贴您的 `.gitconfig` 文件内容,或仅包含 `[user]` 部分的核心配置。
26+
* **启用 SSH** (可选):
27+
* 如果您的 Git 仓库使用 SSH 协议,请勾选此项。
28+
* **主机名**:填写 Git 平台的主机名,如 `github.com`
29+
* **SSH 私钥路径**:指定与该账号对应的 SSH 私钥文件路径,如 `~/.ssh/id_rsa_work`。您可以点击文件夹图标进行选择。
30+
* 点击“保存”。
31+
3. **切换配置**
32+
* 在主界面列表中,找到您想切换到的配置。
33+
* 点击右侧的“切换”图标 ( ⇄ )。
34+
* 应用将自动完成备份和配置更新,并通过提示消息告知您结果。
35+
4. **备份管理**
36+
* 点击主界面右下角的“备份”图标,进入备份管理页面。
37+
* 这里会按时间顺序列出所有的历史备份。
38+
* 您可以预览任一备份的内容,或选中某个版本进行恢复。
39+
5. **设置**
40+
* 点击主界面右上角的“设置”图标。
41+
* 在这里您可以开关自动备份功能,并设置希望保留的备份文件数量。
42+
43+
## 数据存储
44+
45+
* **配置文件**:应用的所有配置都以 JSON 格式存储在您的用户主目录下的 `.git_switcher` 文件夹中。
46+
* **Windows**: `%USERPROFILE%\.git_switcher\config.json`
47+
* **Linux/macOS**: `~/.git_switcher/config.json`
48+
* **备份文件**:所有的备份文件也存放在 `.git_switcher/backup` 目录下,并按 git 和 ssh 分类。
49+
50+
## License
51+
52+
本软件使用 **GNU General Public License v2.0** 授权。
53+

analysis_options.yaml

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
# This file configures the analyzer, which statically analyzes Dart code to
2+
# check for errors, warnings, and lints.
3+
#
4+
# The issues identified by the analyzer are surfaced in the UI of Dart-enabled
5+
# IDEs (https://dart.dev/tools#ides-and-editors). The analyzer can also be
6+
# invoked from the command line by running `flutter analyze`.
7+
8+
# The following line activates a set of recommended lints for Flutter apps,
9+
# packages, and plugins designed to encourage good coding practices.
10+
include: package:flutter_lints/flutter.yaml
11+
12+
linter:
13+
# The lint rules applied to this project can be customized in the
14+
# section below to disable rules from the `package:flutter_lints/flutter.yaml`
15+
# included above or to enable additional rules. A list of all available lints
16+
# and their documentation is published at https://dart.dev/lints.
17+
#
18+
# Instead of disabling a lint rule for the entire project in the
19+
# section below, it can also be suppressed for a single line of code
20+
# or a specific dart file by using the `// ignore: name_of_lint` and
21+
# `// ignore_for_file: name_of_lint` syntax on the line or in the file
22+
# producing the lint.
23+
rules:
24+
# avoid_print: false # Uncomment to disable the `avoid_print` rule
25+
# prefer_single_quotes: true # Uncomment to enable the `prefer_single_quotes` rule
26+
27+
# Additional information about this file can be found at
28+
# https://dart.dev/guides/language/analysis-options

assets/fonts/AlibabaPuHuiTi.ttf

8.14 MB
Binary file not shown.

assets/img/app_icon.ico

33 KB
Binary file not shown.

assets/img/app_icon.png

8.57 KB
Loading

lib/main.dart

Lines changed: 189 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,189 @@
1+
import 'dart:io';
2+
import 'package:flutter/gestures.dart';
3+
import 'package:flutter/material.dart';
4+
import 'package:tray_manager/tray_manager.dart';
5+
import 'package:url_launcher/url_launcher.dart';
6+
import 'package:window_manager/window_manager.dart';
7+
import 'pages/home_page.dart';
8+
import 'services/config_service.dart';
9+
10+
final GlobalKey<NavigatorState> navigatorKey = GlobalKey<NavigatorState>();
11+
12+
void main() async {
13+
WidgetsFlutterBinding.ensureInitialized();
14+
await ConfigService.instance.initialize();
15+
await windowManager.ensureInitialized();
16+
17+
WindowOptions windowOptions = const WindowOptions(
18+
size: Size(800, 600),
19+
center: true,
20+
backgroundColor: Colors.transparent,
21+
skipTaskbar: false,
22+
titleBarStyle: TitleBarStyle.normal,
23+
);
24+
25+
windowManager.waitUntilReadyToShow(windowOptions, () async {
26+
await windowManager.show();
27+
await windowManager.focus();
28+
runApp(const GitSwitcherApp());
29+
});
30+
}
31+
32+
class GitSwitcherApp extends StatefulWidget {
33+
const GitSwitcherApp({super.key});
34+
35+
@override
36+
State<GitSwitcherApp> createState() => _GitSwitcherAppState();
37+
}
38+
39+
class _GitSwitcherAppState extends State<GitSwitcherApp>
40+
with WindowListener, TrayListener {
41+
@override
42+
void initState() {
43+
super.initState();
44+
windowManager.addListener(this);
45+
trayManager.addListener(this);
46+
windowManager.setPreventClose(true);
47+
_initTray();
48+
}
49+
50+
@override
51+
void dispose() {
52+
windowManager.removeListener(this);
53+
trayManager.removeListener(this);
54+
super.dispose();
55+
}
56+
57+
void _initTray() async {
58+
String iconPath = Platform.isWindows
59+
? 'assets/img/app_icon.ico'
60+
: 'assets/img/app_icon.png';
61+
await trayManager.setIcon(iconPath);
62+
63+
List<MenuItem> items = [
64+
MenuItem(key: 'about_app', label: '关于'),
65+
MenuItem.separator(),
66+
MenuItem(key: 'exit_app', label: '退出'),
67+
];
68+
69+
await trayManager.setContextMenu(Menu(items: items));
70+
trayManager.setToolTip('Git Switcher');
71+
72+
await trayManager.setContextMenu(Menu(items: items));
73+
trayManager.setToolTip('Git Switcher');
74+
}
75+
76+
void _showAboutDialog() {
77+
final context = navigatorKey.currentContext;
78+
if (context == null) return;
79+
80+
final uriGithub = Uri.parse('https://github.com/voidbytes/git-switcher');
81+
final uriHomepage = Uri.parse('http://voidbytes.com/');
82+
83+
showDialog(
84+
context: context,
85+
builder: (dialogContext) => AlertDialog(
86+
title: const Text('关于 Git Switcher'),
87+
content: RichText(
88+
text: TextSpan(
89+
style: Theme.of(context).textTheme.bodyMedium,
90+
children: [
91+
const TextSpan(text: '作者: voidbytes\n'),
92+
const TextSpan(text: '作者主页:'),
93+
TextSpan(
94+
text: 'https://voidbytes.com\n',
95+
style: const TextStyle(
96+
color: Colors.blue,
97+
decoration: TextDecoration.underline,
98+
),
99+
recognizer: TapGestureRecognizer()
100+
..onTap = () {
101+
launchUrl(uriHomepage);
102+
},
103+
),
104+
const TextSpan(text: '项目地址:'),
105+
TextSpan(
106+
text: 'https://github.com/voidbytes/git-switcher',
107+
style: const TextStyle(
108+
color: Colors.blue,
109+
decoration: TextDecoration.underline,
110+
),
111+
recognizer: TapGestureRecognizer()
112+
..onTap = () {
113+
launchUrl(uriGithub);
114+
},
115+
),
116+
],
117+
),
118+
),
119+
actions: [
120+
TextButton(
121+
onPressed: () => Navigator.of(dialogContext).pop(),
122+
child: const Text('关闭'),
123+
),
124+
],
125+
),
126+
);
127+
}
128+
129+
@override
130+
Widget build(BuildContext context) {
131+
return MaterialApp(
132+
navigatorKey: navigatorKey,
133+
title: 'Git账号切换器',
134+
theme: ThemeData(
135+
primarySwatch: Colors.blue,
136+
visualDensity: VisualDensity.comfortable,
137+
fontFamily: "AlibabaPuHuiTi",
138+
fontFamilyFallback: ["AlibabaPuHuiTi"],
139+
colorScheme: ColorScheme.fromSeed(
140+
brightness: Brightness.light,
141+
seedColor: Colors.lightBlueAccent,
142+
),
143+
useMaterial3: true,
144+
),
145+
home: const HomePage(),
146+
);
147+
}
148+
149+
@override
150+
void onWindowClose() {
151+
if (ConfigService.instance.appConfig.minimizeToTray) {
152+
windowManager.hide();
153+
} else {
154+
windowManager.destroy();
155+
}
156+
}
157+
158+
@override
159+
void onTrayIconMouseDown() {
160+
if (Platform.isWindows) {
161+
windowManager.show();
162+
} else {
163+
trayManager.popUpContextMenu();
164+
}
165+
}
166+
167+
@override
168+
void onTrayIconRightMouseDown() {
169+
if (Platform.isWindows) {
170+
trayManager.popUpContextMenu();
171+
}
172+
}
173+
174+
@override
175+
void onTrayMenuItemClick(MenuItem menuItem) {
176+
switch (menuItem.key) {
177+
case 'show_window':
178+
windowManager.show();
179+
break;
180+
case 'about_app':
181+
windowManager.show();
182+
_showAboutDialog();
183+
break;
184+
case 'exit_app':
185+
windowManager.destroy();
186+
break;
187+
}
188+
}
189+
}

0 commit comments

Comments
 (0)