Skip to content

Commit eca46c8

Browse files
committed
feat!: support hj clone --fork
1 parent b86f51d commit eca46c8

File tree

8 files changed

+82
-34
lines changed

8 files changed

+82
-34
lines changed

docs/.vitepress/config.mts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,9 @@ export default defineConfig({
7777

7878
socialLinks: [
7979
{ icon: 'github', link: 'https://github.com/gaojunran/hj' }
80-
]
80+
],
81+
82+
outline: [2, 3]
8183
}
8284
}
8385
},

docs/cn/clone.md

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,9 @@ hj clone https://github.com/gaojunran/hj.git
2626

2727
来克隆一个仓库。
2828

29-
这样写并不简练!你可以用仓库全名来替代一个长 url:
29+
### 使用仓库全名替代 URL
30+
31+
使用 URL 并不简练!你需要去网页中手动复制粘贴一个地址。你可以用仓库全名来替代一个长 URL:
3032

3133
```sh
3234
hj clone gaojunran/hj
@@ -48,7 +50,7 @@ hj clone gaojunran/hj # 默认 host 是 gitlab.com
4850
hj clone hj # 默认 user 是 gaojunran
4951
```
5052

51-
---
53+
### 指定目标文件夹
5254

5355
也可以指定目标文件夹路径。默认是远程仓库的名字。如果你这样执行:
5456

@@ -58,6 +60,16 @@ hj clone gaojunran/hj . # 关注最后的 . 表示当前文件夹
5860

5961
那么仓库内容将被克隆在当前文件夹内,而不是新建一个文件夹。
6062

63+
### Fork 并克隆
64+
65+
对于一个 GitHub 仓库,你可以用一行命令 Fork 某个仓库并克隆自己的分叉:
66+
67+
```sh
68+
hj clone gaojunran/hj --fork
69+
```
70+
71+
---
72+
6173
## 下载仓库
6274

6375
很多情况下我们使用 GitHub 上的仓库,并不需要其历史版本,例如:

docs/cn/commit.md

Lines changed: 19 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,8 @@ hj commit "feat: add new feature"
4646

4747
:::
4848

49+
---
50+
4951
## 重命名提交
5052

5153
::: details 对于熟悉 git 的用户
@@ -68,6 +70,8 @@ hj describe REVSET -m "new message"
6870

6971
`REVSET` 处填写一个具体的提交,如 `@-` 表示最近的一次提交;`-m` 参数填写新的提交描述信息。
7072

73+
---
74+
7175
## 为提交增补变更
7276

7377
::: details 对于熟悉 git 的用户
@@ -217,6 +221,19 @@ hj squash -i --from A --into B
217221

218222
---
219223

224+
225+
## 从提交中移除变更
226+
227+
上文中我们提到了 `hj reset` 可以把一个提交中的变更(默认为最新提交)撤回到工作副本。还有一个命令:
228+
229+
```sh
230+
hj throw @-
231+
```
232+
233+
可以交互式地将一个提交中的变更删除。它不会被移动到某个提交中,而是被丢弃掉。如果不指定提交,默认是工作副本提交。
234+
235+
---
236+
220237
## 拆分提交
221238

222239
::: details 对于熟悉 git 的用户
@@ -252,6 +269,8 @@ hj split -r A -d B
252269

253270
更多用法参见 [jj split](https://jj-vcs.github.io/jj/latest/cli-reference/#jj-split)
254271

272+
---
273+
255274
## 复制提交
256275

257276
::: details 对于熟悉 git 的用户
@@ -331,15 +350,3 @@ hj abandon REVSET
331350
这将移除这个提交,并将其子提交变基到其父提交上。
332351

333352
特别地,如果不指定 `REVSET`,将默认为工作副本(`@`)。此时会清空工作副本的所有变更,并自动地创建一个新工作副本提交。
334-
335-
---
336-
337-
## 从提交中移除变更
338-
339-
上文中我们提到了 `hj reset` 可以把一个提交中的变更(默认为最新提交)撤回到工作副本。还有一个命令:
340-
341-
```sh
342-
hj throw @-
343-
```
344-
345-
可以交互式地将一个提交中的变更删除。它不会被移动到某个提交中,而是被丢弃掉。如果不指定提交,默认是工作副本提交。

docs/cn/faq.md

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -76,17 +76,17 @@ hj 的理念有所不同,「简单」是我们的第一设计理念。我们
7676
> ✅ 表示已经实现,🚧 表示正在实现,🤔 表示未来计划。你也可以在 [Issues](https://github.com/gaojunran/hj/issues) 里提出你的想法!
7777
7878
- ✅ `clone` 支持省略 `owner`
79-
- 🚧 `hj clone --fork`
79+
- ✅ `hj clone --fork`
80+
- ✅ `hj commit``hj describe` 时支持打开编辑器进行多行描述的编辑。
81+
- ✅ 提供 Hooks 支持。
82+
- 🤔 发布到 scoop、homebrew;编写 PowerShell script 和 bash script。
8083
- 🤔 `hj download` 下载单个文件(夹) 时直接下载到指定的目录,而不包含远程仓库中的文件夹结构。
8184
- 🤔 `hj init` 时支持下载 `.gitignore`。([来源](https://github.com/github/gitignore)
8285
- 🤔 全面支持 GitLab。
83-
- ✅ `hj commit``hj describe` 时支持打开编辑器进行多行描述的编辑。
8486
- 🤔 `hj rollback`:交互式选择要回退的命令。
8587
- 🤔 优化与 Git 仓库共存时的体验。
86-
- 🤔 发布到 scoop、homebrew;编写 PowerShell script 和 bash script。
8788
- 🤔 提供更多与 `hj log` 相关的简化命令。
8889
- 🤔 完善的 Stacked PR 支持。
89-
- 🚧 提供 Hooks 支持。
9090

9191
## hj 是如何实现的?
9292

hj.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
11
[hooks]
2-
post_commit = "just install" # use just to install hj to PATH automatically
2+
post_commit = "just post-commit" # use just to install hj to PATH automatically

src/clone.rs

Lines changed: 31 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -8,13 +8,19 @@ pub(crate) fn command_clone(
88
source: &str,
99
destination: Option<&str>,
1010
colocate: bool,
11+
fork: bool,
1112
) -> anyhow::Result<()> {
12-
let url = build_url(
13+
let (mut url, mut user, _) = build_info(
1314
&config.clone.default_host,
1415
config.clone.default_user.as_deref(),
1516
source,
1617
)
1718
.ok_or(anyhow::anyhow!("Invalid URL or fullname"))?;
19+
if fork && let Some(forker) = &config.clone.default_user {
20+
cmd!("gh", "repo", "fork", &url).run()?;
21+
url = url.replacen(&(user.clone() + "/"), &(forker.clone() + "/"), 1);
22+
user = forker.to_string(); // unused now
23+
}
1824
let mut args = vec!["git", "clone", &url];
1925
if let Some(destination) = destination {
2026
args.push(destination);
@@ -26,24 +32,37 @@ pub(crate) fn command_clone(
2632
Ok(())
2733
}
2834

29-
fn build_url(
35+
pub(crate) fn build_info(
3036
default_host: &str,
3137
default_user: Option<&str>,
3238
url_or_fullname: &str,
33-
) -> Option<String> {
34-
let re_url = Regex::new(r"^https://[^\s]*?\.git$").unwrap();
35-
let re_fullname = Regex::new(r"^[^/]+/[^/]+$").unwrap();
39+
) -> Option<(String, String, String)> {
40+
let re_url = Regex::new(r"^https://([^/]+)/([^/]+)/([^.]+)\.git$").unwrap();
41+
let re_fullname = Regex::new(r"^([^/]+)/([^/]+)$").unwrap();
3642
let re_repo = Regex::new(r"^[^/]+$").unwrap();
3743

38-
if re_url.is_match(url_or_fullname) {
39-
Some(url_or_fullname.to_string())
40-
} else if re_fullname.is_match(url_or_fullname) {
44+
if let Some(caps) = re_url.captures(url_or_fullname) {
45+
// let _host = caps.get(1).unwrap().as_str();
46+
let user = caps.get(2).unwrap().as_str();
47+
let repo = caps.get(3).unwrap().as_str();
48+
Some((
49+
url_or_fullname.to_string(),
50+
user.to_string(),
51+
repo.to_string(),
52+
))
53+
} else if let Some(caps) = re_fullname.captures(url_or_fullname) {
4154
// user/repo
42-
Some(format!("https://{}/{}.git", default_host, url_or_fullname))
55+
let user = caps.get(1).unwrap().as_str();
56+
let repo = caps.get(2).unwrap().as_str();
57+
let url = format!("https://{}/{}/{}.git", default_host, user, repo);
58+
Some((url, user.to_string(), repo.to_string()))
4359
} else if re_repo.is_match(url_or_fullname) {
44-
// pass repo only, needs `default_user`
45-
default_user
46-
.map(|user| format!("https://{}/{}/{}.git", default_host, user, url_or_fullname))
60+
// repo only
61+
default_user.map(|user| {
62+
let repo = url_or_fullname;
63+
let url = format!("https://{}/{}/{}.git", default_host, user, repo);
64+
(url, user.to_string(), repo.to_string())
65+
})
4766
} else {
4867
None
4968
}

src/download.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ use std::io::Write;
1010
use std::path::{Path, PathBuf};
1111
use tar::Archive;
1212

13+
use crate::clone::build_info;
1314
use crate::config::AppConfig;
1415

1516
#[derive(Debug, Deserialize)]
@@ -30,7 +31,8 @@ pub(crate) fn command_download(
3031
destination: Option<&str>,
3132
entries: Vec<String>,
3233
) -> Result<()> {
33-
let (owner, repo) = parse_repo(source).context("Invalid URL or fullname")?;
34+
let (_, owner, repo) = build_info("", config.clone.default_user.as_deref(), source)
35+
.context("Invalid URL or fullname")?;
3436
let base_path = destination.unwrap_or(&repo);
3537

3638
let mut headers = HeaderMap::new();

src/main.rs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,10 @@ enum Commands {
7474
/// Whether to colocate or not.
7575
#[arg(short, long, alias = "git")]
7676
colocate: bool,
77+
78+
/// Fork a github repo and clone your fork
79+
#[arg(short, long)]
80+
fork: bool,
7781
},
7882

7983
/// Create a commit.
@@ -304,8 +308,10 @@ fn main() {
304308
source,
305309
destination,
306310
colocate,
311+
fork,
307312
} => {
308-
if let Err(e) = command_clone(&config, source, destination.as_deref(), *colocate) {
313+
if let Err(e) = command_clone(&config, source, destination.as_deref(), *colocate, *fork)
314+
{
309315
error(&e.to_string());
310316
}
311317
}

0 commit comments

Comments
 (0)